用Swift写服务端 — Perfect框架

2019-01-15 16:17:35 浏览数 (1)

用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端口:

代码语言:javascript复制
  1. git clone https://github.com/PerfectlySoft/PerfectTemplate.git
  2. cd PerfectTemplate
  3. swift build
  4. .build/debug/PerfectTemplate

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

代码语言:javascript复制
Starting HTTP server on 0.0.0.0:8181 with document root ./webroot

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

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

2.Xcode管理

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

代码语言:javascript复制
swift package generate-xcodeproj

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

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

三、搭建HTTP服务器

编辑main.swift文件

代码语言:javascript复制
  1. import PerfectLib
  2. import PerfectHTTP
  3. import PerfectHTTPServer
  4. //HTTP服务
  5. var routesArr = [Dictionary<String, Any>]()
  6. var someDict1 : [String:String] = ["method":"GET","url":"/api"]
  7. routesArr.append(someDict1)
  8. let networkServer = NetworkServerManager(root: "webroot", port: 8080, routesArr: routesArr)
  9. networkServer.startServer()

创建NetworkServerManager.swift文件

代码语言:javascript复制
  1. //
  2. // NetworkServerManager.swift
  3. // PerfectTemplatePackageDescription
  4. //
  5. // Created by ZFJ on 2018/1/9.
  6. //
  7. import PerfectLib
  8. import PerfectHTTP
  9. import PerfectHTTPServer
  10. open class NetworkServerManager {
  11. fileprivate var server: HTTPServer
  12. internal init(root: String, port: UInt16, routesArr: Array<Dictionary<String, Any>>) {
  13. server = HTTPServer.init() //创建HTTPServer服务器
  14. for dict: Dictionary in routesArr {
  15. let baseUri : String = dict["url"] as! String //跟地址
  16. let method : String = dict["method"] as! String //方法
  17. var routes = Routes.init(baseUri: baseUri) //创建路由器
  18. let httpMethod = HTTPMethod.from(string: method)
  19. configure(routes: &routes, method: httpMethod) //注册路由
  20. server.addRoutes(routes) //路由添加进服务
  21. }
  22. server.serverName = "localhost" //服务器名称
  23. server.serverPort = port //端口
  24. server.documentRoot = root //根目录
  25. server.setResponseFilters([(Filter404(), .high)]) //404过滤
  26. }
  27. //MARK: 开启服务
  28. open func startServer() {
  29. do {
  30. print("启动HTTP服务器")
  31. try server.start()
  32. } catch PerfectError.networkError(let err, let msg) {
  33. print("网络出现错误:(err) (msg)")
  34. } catch {
  35. print("网络未知错误")
  36. }
  37. }
  38. //MARK: 注册路由
  39. fileprivate func configure(routes: inout Routes,method: HTTPMethod) {
  40. routes.add(method: .get, uri: "/selectUserInfor") { (request, response) in
  41. let path = request.path
  42. print(path)
  43. let jsonDic = ["hello": "world"]
  44. let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)
  45. response.setBody(string: jsonString) //响应体
  46. response.completed() //响应
  47. }
  48. // if method == .get{
  49. // //get请求
  50. // }else if method == .post{
  51. // //post请求
  52. // let postParams = request.postParams
  53. // print(postParams)
  54. // }
  55. }
  56. //MARK: 通用响应格式
  57. func baseResponseBodyJSONData(code: Int, message: String, data: Any!) -> String {
  58. var result = Dictionary<String, Any>()
  59. result.updateValue(code, forKey: "code")
  60. result.updateValue(message, forKey: "message")
  61. if (data != nil) {
  62. result.updateValue(data, forKey: "data")
  63. }else{
  64. result.updateValue("", forKey: "data")
  65. }
  66. guard let jsonString = try? result.jsonEncodedString() else {
  67. return ""
  68. }
  69. return jsonString
  70. }
  71. //MARK: 404过滤
  72. struct Filter404: HTTPResponseFilter {
  73. func filterBody(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
  74. callback(.continue)
  75. }
  76. func filterHeaders(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
  77. if case .notFound = response.status {
  78. response.setBody(string: "404 文件(response.request.path)不存在。")
  79. response.setHeader(.contentLength, value: "(response.bodyBytes.count)")
  80. callback(.done)
  81. } else {
  82. callback(.continue)
  83. }
  84. }
  85. }
  86. }

运行结果

接口访问

四、搭建MySql服务器

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

代码语言:javascript复制
brew install mysql

配置MySql

代码语言:javascript复制
  1. #开启MySQL服务
  2. mysql.server start
  3. #初始化MySQL配置向导
  4. 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数据库管理类,在这里我们对数据库进行增删改查操作;

代码语言:javascript复制
  1. //
  2. // DataBaseManager.swift
  3. // PerfectTemplatePackageDescription
  4. //
  5. // Created by ZFJ on 2018/1/17.
  6. //
  7. import MySQL
  8. //MARK: 数据库信息
  9. let mysql_host = "127.0.0.1"
  10. let mysql_user = "root"
  11. let mysql_password = "12345678"
  12. let mysql_database = "userInforsTable"
  13. //MARK: 表信息
  14. let userTable = "userTable" //用户信息表
  15. open class DataBaseManager {
  16. fileprivate var mysql : MySQL
  17. internal init() {
  18. mysql = MySQL.init() //创建MySQL对象
  19. guard connectDataBase() else{ //开启MySQL连接
  20. return
  21. }
  22. }
  23. //MARK:开启链接
  24. private func connectDataBase() -> Bool{
  25. let connected = mysql.connect(host: mysql_host, user: mysql_user, password: mysql_password, db: mysql_database)
  26. guard connected else{
  27. print("MySql链接失败" mysql.errorMessage())
  28. return false
  29. }
  30. print("MySql链接成功")
  31. return true
  32. }
  33. //MARK: 执行SQL语句
  34. /// 执行SQL语句
  35. ///
  36. /// - Parameter sql: sql语句
  37. /// - Returns: 返回元组(success:是否成功 result:结果)
  38. @discardableResult
  39. func mysqlStatement(_ sql:String) -> (success:Bool,mysqlResult:MySQL.Results?,errorMsg:String) {
  40. guard mysql.selectDatabase(named:mysql_database) else {
  41. //指定操作的数据库
  42. let msg = "未找到(mysql_database)数据库"
  43. print(msg)
  44. return(false, nil, msg)
  45. }
  46. let successQuery = mysql.query(statement:sql) //sql语句
  47. guard successQuery else{
  48. let msg = "SQL失败:(sql)"
  49. print(msg)
  50. return(false, nil, msg)
  51. }
  52. let msg = "SQL成功:(sql)"
  53. print(msg)
  54. return (true, mysql.storeResults(), msg) //sql执行成功
  55. }
  56. /// 增
  57. ///
  58. /// - Parameters:
  59. /// - tableName: 表
  60. /// - keyValueDict: 键:值 对字典
  61. func insertDataBaseSQL(tableName:String, keyValueDict:Dictionary<String, Any>) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
  62. var keys: [String] = []
  63. var values: [String] = []
  64. for (key, value) in keyValueDict {
  65. if let str = value as? String {
  66. keys.append(key)
  67. values.append(str)
  68. }
  69. }
  70. let fieldNameAll: String = keys.joined(separator: ",")
  71. let valueAll: String = values.joined(separator: ",")
  72. let SQL = "insert into (tableName)((fieldNameAll)) values((valueAll))"
  73. return mysqlStatement(SQL)
  74. }
  75. /// 删
  76. ///
  77. /// - Parameters:
  78. /// - tableName: 表
  79. /// - key: 键
  80. /// - value: 值
  81. func deleteDatabaseSQL(tableName: String, key: String, value: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
  82. let SQL = "DELETE FROM (tableName) WHERE (key) = '(value)'"
  83. return mysqlStatement(SQL)
  84. }
  85. /// 改
  86. ///
  87. /// - Parameters:
  88. /// - tableName: 表
  89. /// - keyValue: 键值对( 键='值', 键='值', 键='值' )
  90. /// - whereKey: 查找key
  91. /// - whereValue: 查找value
  92. func updateDatabaseSQL(tableName: String, keyValue: String, whereKey: String, whereValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
  93. let SQL = "UPDATE (tableName) SET (keyValue) WHERE (whereKey) = '(whereValue)'"
  94. return mysqlStatement(SQL)
  95. }
  96. /// 查所有
  97. ///
  98. /// - Parameters:
  99. /// - tableName: 表
  100. /// - key: 键
  101. func selectAllDatabaseSQL(tableName: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
  102. let SQL = "SELECT * FROM (tableName)"
  103. return mysqlStatement(SQL)
  104. }
  105. /// 查
  106. ///
  107. /// - Parameters:
  108. /// - tableName: 表
  109. /// - keyValue: 键值对
  110. func selectAllDataBaseSQLwhere(tableName: String, keyValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {
  111. let SQL = "SELECT * FROM (tableName) WHERE (keyValue)"
  112. return mysqlStatement(SQL)
  113. }
  114. //获取表中所有数据
  115. func mysqlGetHomeDataResult() -> [Dictionary<String, String>]? {
  116. let result = selectAllDatabaseSQL(tableName: userTable)
  117. var resultArray = [Dictionary<String, String>]()
  118. var dic = [String:String]()
  119. result.mysqlResult?.forEachRow(callback: { (row) in
  120. dic["userid"] = row[0]
  121. dic["userNumber"] = row[1]
  122. dic["userName"] = row[2]
  123. dic["userSex"] = row[3]
  124. dic["userBirthday"] = row[4]
  125. resultArray.append(dic)
  126. })
  127. return resultArray
  128. }
  129. }

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

代码语言:javascript复制
  1. //MARK: 注册路由
  2. fileprivate func configure(routes: inout Routes,method: HTTPMethod) {
  3. routes.add(method: .get, uri: "/selectUserInfor") { (request, response) in
  4. let path = request.path
  5. print(path)
  6. // let jsonDic = ["hello": "world"]
  7. // let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)
  8. // response.setBody(string: jsonString) //响应体
  9. // response.completed() //响应
  10. let queryParams = request.queryParams
  11. if queryParams.count == 0{
  12. let result = DataBaseManager().mysqlGetHomeDataResult()
  13. let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: result)
  14. response.setBody(string: jsonString)
  15. response.completed()
  16. }else{
  17. //有参数
  18. //let value : String
  19. for i in 0...queryParams.count - 1{
  20. let partArr = queryParams[i]
  21. print(partArr)
  22. }
  23. let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: nil)
  24. response.setBody(string: jsonString)
  25. response.completed()
  26. }
  27. }
  28. }

然后调取接口访问数据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 

示例如下:

代码语言:javascript复制
  1. import PackageDescription
  2. let package = Package(
  3. name: "PerfectTemplate",
  4. targets: [],
  5. dependencies: [
  6. .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 3),
  7. .Package(url: "https://github.com/PerfectlySoft/Perfect-MySQL.git", majorVersion: 2),
  8. ]
  9. )

然后删除PerfectTemplate.xcodeproj文件, 接着终端重新生成PerfectTemplate.xcodeproj文件,最后打开工程就会发现MySQL库了,如下图:

DEMO下载

点击下载(http://download.csdn.net/download/u014220518/10240141)

--------------------- 本文来自 ZFJ_张福杰 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u014220518/article/details/79217903?utm_source=copy 

0 人点赞