1. 程式人生 > >用Swift寫服務端 — Perfect框架

用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埠:

 

 
  1. git clone https://github.com/PerfectlySoft/PerfectTemplate.git

  2. cd PerfectTemplate

  3. swift build

  4. .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檔案

 

 
  1. import PerfectLib

  2. import PerfectHTTP

  3. import PerfectHTTPServer

  4.  
  5. //HTTP服務

  6. var routesArr = [Dictionary<String, Any>]()

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

  9.  
  10. routesArr.append(someDict1)

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

  13.  
  14. networkServer.startServer()

建立NetworkServerManager.swift檔案

 

 

 
  1. //

  2. // NetworkServerManager.swift

  3. // PerfectTemplatePackageDescription

  4. //

  5. // Created by ZFJ on 2018/1/9.

  6. //

  7.  
  8. import PerfectLib

  9. import PerfectHTTP

  10. import PerfectHTTPServer

  11.  
  12. open class NetworkServerManager {

  13. fileprivate var server: HTTPServer

  14. internal init(root: String, port: UInt16, routesArr: Array<Dictionary<String, Any>>) {

  15. server = HTTPServer.init() //建立HTTPServer伺服器

  16. for dict: Dictionary in routesArr {

  17. let baseUri : String = dict["url"] as! String //跟地址

  18. let method : String = dict["method"] as! String //方法

  19. var routes = Routes.init(baseUri: baseUri) //建立路由器

  20. let httpMethod = HTTPMethod.from(string: method)

  21. configure(routes: &routes, method: httpMethod) //註冊路由

  22. server.addRoutes(routes) //路由新增進服務

  23. }

  24. server.serverName = "localhost" //伺服器名稱

  25. server.serverPort = port //埠

  26. server.documentRoot = root //根目錄

  27. server.setResponseFilters([(Filter404(), .high)]) //404過濾

  28. }

  29.  
  30. //MARK: 開啟服務

  31. open func startServer() {

  32. do {

  33. print("啟動HTTP伺服器")

  34. try server.start()

  35. } catch PerfectError.networkError(let err, let msg) {

  36. print("網路出現錯誤:\(err) \(msg)")

  37. } catch {

  38. print("網路未知錯誤")

  39. }

  40.  
  41. }

  42.  
  43. //MARK: 註冊路由

  44. fileprivate func configure(routes: inout Routes,method: HTTPMethod) {

  45. routes.add(method: .get, uri: "/selectUserInfor") { (request, response) in

  46. let path = request.path

  47. print(path)

  48. let jsonDic = ["hello": "world"]

  49. let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)

  50. response.setBody(string: jsonString) //響應體

  51. response.completed() //響應

  52. }

  53.  
  54. // if method == .get{

  55. // //get請求

  56. // }else if method == .post{

  57. // //post請求

  58. // let postParams = request.postParams

  59. // print(postParams)

  60. // }

  61. }

  62.  
  63. //MARK: 通用響應格式

  64. func baseResponseBodyJSONData(code: Int, message: String, data: Any!) -> String {

  65. var result = Dictionary<String, Any>()

  66. result.updateValue(code, forKey: "code")

  67. result.updateValue(message, forKey: "message")

  68. if (data != nil) {

  69. result.updateValue(data, forKey: "data")

  70. }else{

  71. result.updateValue("", forKey: "data")

  72. }

  73. guard let jsonString = try? result.jsonEncodedString() else {

  74. return ""

  75. }

  76. return jsonString

  77. }

  78.  
  79. //MARK: 404過濾

  80. struct Filter404: HTTPResponseFilter {

  81. func filterBody(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {

  82. callback(.continue)

  83. }

  84. func filterHeaders(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {

  85. if case .notFound = response.status {

  86. response.setBody(string: "404 檔案\(response.request.path)不存在。")

  87. response.setHeader(.contentLength, value: "\(response.bodyBytes.count)")

  88. callback(.done)

  89. } else {

  90. callback(.continue)

  91. }

  92. }

  93.  
  94. }

  95. }

  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  

執行結果

 



 

 

介面訪問

四、搭建MySql伺服器

我的電腦上安裝的有Homebrew,所以我直接通過Homebrew安裝MySql,安裝命令:

 

brew install mysql


配置MySql

 

 

 
  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資料庫管理類,在這裡我們對資料庫進行增刪改查操作;

 

 
  1. //

  2. // DataBaseManager.swift

  3. // PerfectTemplatePackageDescription

  4. //

  5. // Created by ZFJ on 2018/1/17.

  6. //

  7.  
  8. import MySQL

  9.  
  10. //MARK: 資料庫資訊

  11. let mysql_host = "127.0.0.1"

  12. let mysql_user = "root"

  13. let mysql_password = "12345678"

  14. let mysql_database = "userInforsTable"

  15.  
  16. //MARK: 表資訊

  17. let userTable = "userTable" //使用者資訊表

  18.  
  19. open class DataBaseManager {

  20. fileprivate var mysql : MySQL

  21. internal init() {

  22. mysql = MySQL.init() //建立MySQL物件

  23. guard connectDataBase() else{ //開啟MySQL連線

  24. return

  25. }

  26. }

  27.  
  28. //MARK:開啟連結

  29. private func connectDataBase() -> Bool{

  30. let connected = mysql.connect(host: mysql_host, user: mysql_user, password: mysql_password, db: mysql_database)

  31. guard connected else{

  32. print("MySql連結失敗" + mysql.errorMessage())

  33. return false

  34. }

  35. print("MySql連結成功")

  36. return true

  37. }

  38.  
  39. //MARK: 執行SQL語句

  40. /// 執行SQL語句

  41. ///

  42. /// - Parameter sql: sql語句

  43. /// - Returns: 返回元組(success:是否成功 result:結果)

  44. @discardableResult

  45. func mysqlStatement(_ sql:String) -> (success:Bool,mysqlResult:MySQL.Results?,errorMsg:String) {

  46. guard mysql.selectDatabase(named:mysql_database) else {

  47. //指定操作的資料庫

  48. let msg = "未找到\(mysql_database)資料庫"

  49. print(msg)

  50. return(false, nil, msg)

  51. }

  52.  
  53. let successQuery = mysql.query(statement:sql) //sql語句

  54. guard successQuery else{

  55. let msg = "SQL失敗:\(sql)"

  56. print(msg)

  57. return(false, nil, msg)

  58. }

  59. let msg = "SQL成功:\(sql)"

  60. print(msg)

  61. return (true, mysql.storeResults(), msg) //sql執行成功

  62. }

  63.  
  64. /// 增

  65. ///

  66. /// - Parameters:

  67. /// - tableName: 表

  68. /// - keyValueDict: 鍵:值 對字典

  69. func insertDataBaseSQL(tableName:String, keyValueDict:Dictionary<String, Any>) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {

  70. var keys: [String] = []

  71. var values: [String] = []

  72. for (key, value) in keyValueDict {

  73. if let str = value as? String {

  74. keys.append(key)

  75. values.append(str)

  76. }

  77. }

  78. let fieldNameAll: String = keys.joined(separator: ",")

  79. let valueAll: String = values.joined(separator: ",")

  80. let SQL = "insert into \(tableName)(\(fieldNameAll)) values(\(valueAll))"

  81. return mysqlStatement(SQL)

  82. }

  83.  
  84. /// 刪

  85. ///

  86. /// - Parameters:

  87. /// - tableName: 表

  88. /// - key: 鍵

  89. /// - value: 值

  90. func deleteDatabaseSQL(tableName: String, key: String, value: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {

  91.  
  92. let SQL = "DELETE FROM \(tableName) WHERE \(key) = '\(value)'"

  93. return mysqlStatement(SQL)

  94.  
  95. }

  96.  
  97. /// 改

  98. ///

  99. /// - Parameters:

  100. /// - tableName: 表

  101. /// - keyValue: 鍵值對( 鍵='值', 鍵='值', 鍵='值' )

  102. /// - whereKey: 查詢key

  103. /// - whereValue: 查詢value

  104. func updateDatabaseSQL(tableName: String, keyValue: String, whereKey: String, whereValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {

  105.  
  106. let SQL = "UPDATE \(tableName) SET \(keyValue) WHERE \(whereKey) = '\(whereValue)'"

  107. return mysqlStatement(SQL)

  108.  
  109. }

  110.  
  111. /// 查所有

  112. ///

  113. /// - Parameters:

  114. /// - tableName: 表

  115. /// - key: 鍵

  116. func selectAllDatabaseSQL(tableName: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {

  117.  
  118. let SQL = "SELECT * FROM \(tableName)"

  119. return mysqlStatement(SQL)

  120.  
  121. }

  122.  
  123. /// 查

  124. ///

  125. /// - Parameters:

  126. /// - tableName: 表

  127. /// - keyValue: 鍵值對

  128. func selectAllDataBaseSQLwhere(tableName: String, keyValue: String) -> (success: Bool, mysqlResult: MySQL.Results?, errorMsg: String) {

  129.  
  130. let SQL = "SELECT * FROM \(tableName) WHERE \(keyValue)"

  131. return mysqlStatement(SQL)

  132.  
  133. }

  134.  
  135. //獲取表中所有資料

  136. func mysqlGetHomeDataResult() -> [Dictionary<String, String>]? {

  137. let result = selectAllDatabaseSQL(tableName: userTable)

  138. var resultArray = [Dictionary<String, String>]()

  139. var dic = [String:String]()

  140. result.mysqlResult?.forEachRow(callback: { (row) in

  141. dic["userid"] = row[0]

  142. dic["userNumber"] = row[1]

  143. dic["userName"] = row[2]

  144. dic["userSex"] = row[3]

  145. dic["userBirthday"] = row[4]

  146. resultArray.append(dic)

  147. })

  148. return resultArray

  149.  
  150. }

  151. }

  152.  

 

然後在NetworkServerManager中呼叫DataBaseManager,註冊子路由/selectUserInfor查詢使用者表裡的所以資訊;

 

 
  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. }

  29.  

然後調取介面訪問資料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 

示例如下:

 

 
  1. import PackageDescription

  2.  
  3. let package = Package(

  4. name: "PerfectTemplate",

  5. targets: [],

  6. dependencies: [

  7. .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 3),

  8. .Package(url: "https://github.com/PerfectlySoft/Perfect-MySQL.git", majorVersion: 2),

  9. ]

  10. )

然後刪除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