用Swift写服务端 — Perfect框架学习(一)

用Swift写服务端 — Perfect框架学习(一)

一、Perfect简介

Perfect是一组完整、强大的工具箱、软件框架体系和Web应用服务器,可以在Linux、iOS和macOS (OS X)上使用。该软件体系为Swift工程师量身定制了一整套用于开发轻量、易维护、规模可扩展的Web应用及其它REST服务的解决方案,这样Swift工程师就可以实现同时在服务器和客户端上采用同一种语言开发软件项目。

由于建立在一个高性能异步网络引擎基础上,Perfect还能够在FastCGI上运行,支持安全套接字加密(SSL)。该软件体系还包含很多其它互联网服务器所需要的特点,包括WebSockets和iOS消息推送,而且很快会有更多强大的功能支持。


无论您是资深程序员还是入门级的软件工程师,本文都能够帮助您快速启动Perfect实现服务器项目开发运行。

二、Perfect项目快速上手

1.编译入门项目

我们在Perfect官网的git上直接下载一个入门项目。编译后就可以启动一个本地的服务,监听你的8181端口:

git clone https://github.com/PerfectlySoft/PerfectTemplate.git
cd PerfectTemplate
swift build
.build/debug/PerfectTemplate



我们可以在控制台看到以下内容:

Starting HTTP server on 0.0.0.0:8181 with document root ./webroot

服务器现在已经运行并等待连接。从浏览器打开http://localhost:8181/ 可以看到欢迎信息。


 在终端控制台中输入组合键“control-c”可以随时终止服务器运行。


2.Xcode管理

Swift软件包管理器(SPM)能够创建一个Xcode项目,并且能够运行PerfectTemplate模板服务器,还能为您的项目提供完全的源代码编辑和调试。在您的终端命令行内输入:

swift package generate-xcodeproj

然后打开产生的文件“PerfectTemplate.xcodeproj”,确定选择了可执行的目标文件,并选择在“我的Mac”运行。现在您可以运行并调试服务器了。



直接运行XCode,然后在浏览器中输入0.0.0.0:8181也是能直接运行的!

三、搭建HTTP服务器

编辑main.swift文件

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer

//HTTP服务
var routesArr = [Dictionary<String, Any>]()

var someDict1 : [String:String] = ["method":"GET","url":"/api"]

routesArr.append(someDict1)

let networkServer = NetworkServerManager(root: "webroot", port: 8080, routesArr: routesArr)

networkServer.startServer()
创建NetworkServerManager.swift文件

//
//  NetworkServerManager.swift
//  PerfectTemplatePackageDescription
//
//  Created by ZFJ on 2018/1/9.
//

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer

open class NetworkServerManager {
    fileprivate var server: HTTPServer
    internal init(root: String, port: UInt16, routesArr: Array<Dictionary<String, Any>>) {
        server = HTTPServer.init()                             //创建HTTPServer服务器
        for dict: Dictionary in routesArr {
            let baseUri : String = dict["url"] as! String      //跟地址
            let method : String = dict["method"] as! String    //方法
            var routes = Routes.init(baseUri: baseUri)         //创建路由器
            let httpMethod = HTTPMethod.from(string: method)
            configure(routes: &routes, method: httpMethod)     //注册路由
            server.addRoutes(routes)                           //路由添加进服务
        }
        server.serverName = "localhost"                        //服务器名称
        server.serverPort = port                               //端口
        server.documentRoot = root                             //根目录
        server.setResponseFilters([(Filter404(), .high)])      //404过滤
    }
    
    //MARK: 开启服务
    open func startServer() {
        do {
            print("启动HTTP服务器")
            try server.start()
        } catch PerfectError.networkError(let err, let msg) {
            print("网络出现错误:\(err) \(msg)")
        } catch {
            print("网络未知错误")
        }
        
    }
    
    //MARK: 注册路由
    fileprivate func configure(routes: inout Routes,method: HTTPMethod) {
        routes.add(method: .get, uri: "/selectUserInfor") { (request, response) in
            let path = request.path
            print(path)
            let jsonDic = ["hello": "world"]
            let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)
            response.setBody(string: jsonString)                           //响应体
            response.completed()                                           //响应
        }
        
//        if method == .get{
//            //get请求
//        }else if method == .post{
//            //post请求
//            let postParams = request.postParams
//            print(postParams)
//        }
    }
    
    //MARK: 通用响应格式
    func baseResponseBodyJSONData(code: Int, message: String, data: Any!) -> String {
        var result = Dictionary<String, Any>()
        result.updateValue(code, forKey: "code")
        result.updateValue(message, forKey: "message")
        if (data != nil) {
            result.updateValue(data, forKey: "data")
        }else{
            result.updateValue("", forKey: "data")
        }
        guard let jsonString = try? result.jsonEncodedString() else {
            return ""
        }
        return jsonString
    }
    
    //MARK: 404过滤
    struct Filter404: HTTPResponseFilter {
        func filterBody(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
            callback(.continue)
        }
        func filterHeaders(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
            if case .notFound = response.status {
                response.setBody(string: "404 文件\(response.request.path)不存在。")
                response.setHeader(.contentLength, value: "\(response.bodyBytes.count)")
                callback(.done)
            } else {
                callback(.continue)
            }
        }
        
    }
}














运行结果




接口访问


四、搭建MySql服务器

我的电脑上安装的有Homebrew,所以我直接通过Homebrew安装MySql,安装命令:

brew install mysql

配置MySql

#开启MySQL服务
mysql.server start
#初始化MySQL配置向导
mysql_secure_installation
我电脑上数据库已经而配置好了,这里面我就不演示了,如果有不了解的可以加我QQ或者QQ群;


五、安装Navicat Premium

Navicat premium是一款数据库管理工具,是一个可多重连线资料库的管理工具,它可以让你以单一程式同时连线到 MySQL、SQLite、Oracle 及 PostgreSQL 资料库,让管理不同类型的资料库更加的方便。


Navicat Premium_12.0.22破解版下载


这里面下载好了以后会让你输入安装密码,密码为:xclient.info 

如下图:


安装成功以后如果打开出现如下图的错误,只需要在终端输入以下代码就好;


执行以下命令开启
sudo spctl --master-disable


这样就可以打开了,然后链接MySQL数据库,如下图


然后创建数据库userInforsTable,然后创建了一个userTable表,并向userTable表中添加了三条数据;如下图:


这样你就可以操作MySQL数据库了,当然你也可以通过终端直接操作数据库;


六、编辑Perfect服务端

创建DataBaseManager.swift数据库管理类,在这里我们对数据库进行增删改查操作;

//
//  DataBaseManager.swift
//  PerfectTemplatePackageDescription
//
//  Created by ZFJ on 2018/1/17.
//

import MySQL

//MARK: 数据库信息
let mysql_host = "127.0.0.1"
let mysql_user = "root"
let mysql_password = "12345678"
let mysql_database = "userInforsTable"

//MARK: 表信息
let userTable = "userTable"                    //用户信息表

open class DataBaseManager {
    fileprivate var mysql : MySQL
    internal init() {
        mysql = MySQL.init()                       //创建MySQL对象
        guard connectDataBase() else{            //开启MySQL连接
            return
        }
    }
    
    //MARK:开启链接
    private func connectDataBase() -> Bool{
        let connected = mysql.connect(host: mysql_host, user: mysql_user, password: mysql_password, db: mysql_database)
        guard connected else{
            print("MySql链接失败" + mysql.errorMessage())
            return false
        }
        print("MySql链接成功")
        return true
    }
    
    //MARK: 执行SQL语句
    /// 执行SQL语句
    ///
    /// - Parameter sql: sql语句
    /// - Returns: 返回元组(success:是否成功 result:结果)
    @discardableResult
    func mysqlStatement(_ sql:String) -> (success:Bool,mysqlResult:MySQL.Results?,errorMsg:String) {
        guard mysql.selectDatabase(named:mysql_database) else {
            //指定操作的数据库
            let msg = "未找到\(mysql_database)数据库"
            print(msg)
            return(false, nil, msg)
        }
        
        let successQuery = mysql.query(statement:sql) //sql语句
        guard successQuery else{
            let msg = "SQL失败:\(sql)"
            print(msg)
            return(false, nil, msg)
        }
        let msg = "SQL成功:\(sql)"
        print(msg)
        return (true, mysql.storeResults(), msg)                            //sql执行成功
    }
    
    /// 增
    ///
    /// - Parameters:
    ///   - tableName: 表
    ///   - keyValueDict: 键:值 对字典
    func insertDataBaseSQL(tableName:String, keyValueDict:Dictionary<String, Any>) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
        var keys: [String] = []
        var values: [String] = []
        for (key, value) in keyValueDict {
            if let str = value as? String {
                keys.append(key)
                values.append(str)
            }
        }
        let fieldNameAll: String = keys.joined(separator: ",")
        let valueAll: String = values.joined(separator: ",")
        let SQL = "insert into \(tableName)(\(fieldNameAll)) values(\(valueAll))"
        return mysqlStatement(SQL)
    }
    
    /// 删
    ///
    /// - Parameters:
    ///   - tableName: 表
    ///   - key: 键
    ///   - value: 值
    func deleteDatabaseSQL(tableName: String, key: String, value: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
        
        let SQL = "DELETE FROM \(tableName) WHERE \(key) = '\(value)'"
        return mysqlStatement(SQL)
        
    }
    
    /// 改
    ///
    /// - Parameters:
    ///   - tableName: 表
    ///   - keyValue: 键值对( 键='值', 键='值', 键='值' )
    ///   - whereKey: 查找key
    ///   - whereValue: 查找value
    func updateDatabaseSQL(tableName: String, keyValue: String, whereKey: String, whereValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
        
        let SQL = "UPDATE \(tableName) SET \(keyValue) WHERE \(whereKey) = '\(whereValue)'"
        return mysqlStatement(SQL)
        
    }
    
    /// 查所有
    ///
    /// - Parameters:
    ///   - tableName: 表
    ///   - key: 键
    func selectAllDatabaseSQL(tableName: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
        
        let SQL = "SELECT * FROM \(tableName)"
        return mysqlStatement(SQL)
        
    }
    
    /// 查
    ///
    /// - Parameters:
    ///   - tableName: 表
    ///   - keyValue: 键值对
    func selectAllDataBaseSQLwhere(tableName: String, keyValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
        
        let SQL = "SELECT * FROM \(tableName) WHERE \(keyValue)"
        return mysqlStatement(SQL)
        
    }
    
    //获取表中所有数据
    func mysqlGetHomeDataResult() -> [Dictionary<String, String>]? {
        let result = selectAllDatabaseSQL(tableName: userTable)
        var resultArray = [Dictionary<String, String>]()
        var dic = [String:String]()
        result.mysqlResult?.forEachRow(callback: { (row) in
            dic["userid"] = row[0]
            dic["userNumber"] = row[1]
            dic["userName"] = row[2]
            dic["userSex"] = row[3]
            dic["userBirthday"] = row[4]
            resultArray.append(dic)
        })
        return resultArray
        
    }
}

然后在NetworkServerManager中调用DataBaseManager,注册子路由/selectUserInfor查询用户表里的所以信息;

    //MARK: 注册路由
    fileprivate func configure(routes: inout Routes,method: HTTPMethod) {
        routes.add(method: .get, uri: "/selectUserInfor") { (request, response) in
            let path = request.path
            print(path)
//            let jsonDic = ["hello": "world"]
//            let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)
//            response.setBody(string: jsonString)                           //响应体
//            response.completed()                                           //响应
            let queryParams = request.queryParams
            if queryParams.count == 0{
                let result = DataBaseManager().mysqlGetHomeDataResult()
                let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: result)
                response.setBody(string: jsonString)
                response.completed()
            }else{
                //有参数
                //let value : String
                for i in 0...queryParams.count - 1{
                    let partArr = queryParams[i]
                    print(partArr)
                }
                let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: nil)
                response.setBody(string: jsonString)
                response.completed()
            }
        }
    }
    
然后调取接口访问数据http://0.0.0.0:8080/api/selectUserInfor;如下图:


注意事项

1.如果你在NetworkServerManager中无法调用DataBaseManager,或者说调用DataBaseManager查找不到,那是因为你创建DataBaseManager的时候没有选择在项目中引用,默认选择了第一个第三方库了;


如果你创建完成只需要稍微修改一下就好;


2.如果提示MySQL找不到,那是因为你的工程中,或者我们开始下载的那个示例工程没有导入MySQL,你需要引用一下就好;

首先修改Package.swift文件,引用https://github.com/PerfectlySoft/Perfect-MySQL.git 

示例如下:

import PackageDescription

let package = Package(
    name: "PerfectTemplate",
    targets: [],
    dependencies: [
        .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 3),
        .Package(url: "https://github.com/PerfectlySoft/Perfect-MySQL.git", majorVersion: 2),
    ]
)
然后删除PerfectTemplate.xcodeproj文件, 接着终端重新生成PerfectTemplate.xcodeproj文件,最后打开工程就会发现MySQL库了,如下图:


DEMO下载

结束语

欢迎大家加移动开发技术交流群,在这里大家可以一起讨论学习,这里有大佬,也有小菜鸟,没事还能斗斗图装装逼,如果需要换工作的还能相互推荐,期待大家的加入!大笑偷笑


                                                             

                












猜你喜欢

转载自blog.csdn.net/u014220518/article/details/79217903
今日推荐