用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 PerfectHTTPServeropen class NetworkServerManager {fileprivate var server: HTTPServerinternal 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) inlet path = request.pathprint(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 : MySQLinternal 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:结果)@discardableResultfunc 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: 查找valuefunc 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) indic["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) inlet path = request.pathprint(path)
//            let jsonDic = ["hello": "world"]
//            let jsonString = self.baseResponseBodyJSONData(code: 200, message: "成功", data: jsonDic)
//            response.setBody(string: jsonString)                           //响应体
//            response.completed()                                           //响应let queryParams = request.queryParamsif 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 : Stringfor 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 PackageDescriptionlet 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下载

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

结束语

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

用Swift写服务端 — Perfect框架学习(一)相关推荐

  1. 开源分享 Unity3d客户端与C#分布式服务端游戏框架

    很久之前,在博客园写了一篇文章,<分布式网游server的一些想法语言和平台的选择>,当时就有了用C#做网游服务端的想法.写了个Unity3d客户端分布式服务端框架,最近发布了1.0版本, ...

  2. 开发函数计算的正确姿势 —— 移植 next.js 服务端渲染框架

    为什么80%的码农都做不了架构师?>>>    首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算 ...

  3. Angular 服务端渲染(SSR) 学习笔记

    文章目录 1 服务器端渲染 (SSR) 2 SSR的利弊 2.1 SSR的优势 2.2 SSR的局限 3 Angular Universal 3.1 Node.js Express web serve ...

  4. 为什么还有这么多人用C++写服务端?

    现代选择有很多:java + javascript, c+python, c+lua, scala, go, erlang.我们面向性能的服务器用 java,面向逻辑服务器 python,面向高并发的 ...

  5. 游戏开发--开源软件11--Firefly(python 服务端分布式框架)||pygame

    2019独角兽企业重金招聘Python工程师标准>>> Firefly是免费.开源.稳定.快速扩展.能 "热更新"的分布式游戏服务器端框架,采用Python编写, ...

  6. python 服务端性能_python 学习笔记---Locust 测试服务端性能

    由于人工智能的热度, python目前已经成为最受欢迎的编程语言,一度已经超越Java . 本文将介绍开源的python 测试工具: locust 使用步骤: 1. 安装python 3.0以上版本 ...

  7. 最常用、最好用的vue服务端渲染框架

    vue的官方提供了服务端渲染的方法,但没有做成框架发布出来,而是让开发者自己按照里面的步骤一步一步去搭建,然而里面的步骤十分多,要看懂例子也十分困难. 这样大家就遇到了一个问题,读ssr原理本身不熟的 ...

  8. 如何用php向wsdl服务器发请求,知道服务器端Wsdl,不写服务端代码,仅写客户端代码能调用服务端的方法吗?...

    新手请教,望高手朋友不吝赐教,无比感谢中. 今天一大早开始研究Webservice,有一本不太专业的参考书,随便找了一个服务端的Wsdl,想调用其中的方法,结果搞了一天,无果,老报错,郁闷. 代码如下 ...

  9. 项目实训-千寻-服务端SSH框架完善

    不必太多废话,继续上篇文章,继续SSH框架的完善. 1.Spring引入 上次已经把Spring相关的jar包导入了,本次的开端,就是创建Spring的配置文件了.与创建Struts的配置文件比较类似 ...

最新文章

  1. Mastering Algorithms with C中文版附带源码说明
  2. 50-100G大文件的处理办法
  3. PHP key() 函数
  4. JavaScript通用表单验证函数
  5. 终于完成词频统计小程序~
  6. C#生成不重复随机数列表
  7. 笨方法学Python3 习题 0
  8. C++string类常用方法
  9. 服务器IP为什么会被封,以及解决办法
  10. Kaminari分页
  11. 书到用时方恨少,绝知此事要躬行--谈TCP/UDP编程
  12. 写给想做互联网产品经理的师弟师妹们一些话
  13. Android小练习2——制作点菜界面
  14. PHP图片拼接util
  15. 书桌台灯怎么选?分享儿童卧室灯品牌
  16. HttpRequest 介绍
  17. gabor与gabor小波
  18. 对于“2017面向对象程序设计(Java)第五周工作总结”存在问题的反馈及本周教学计划...
  19. 笔记:处理token过期
  20. 【渝粤教育】广东开放大学 公共关系实务 形成性考核 (37)

热门文章

  1. 华为nova5por计算机怎么看历史记录,华为nova5pro返回操作方法
  2. CPU_Z计算机主板性能表,您如何看待CPU-Z的参数? CPU-Z对CPU型号进行全面分析
  3. 桌面调试平台flipper
  4. 在线php序列化解析,PHP序列化serialize
  5. QT5报错之C4819解决方案
  6. could not resolve package...
  7. 【教程】如何在txt文本中提取重复和不重复的手机号码,两个文件两组号码两批手机号对比重复不重复,一键复制和导出
  8. Java锁——什么是锁?
  9. webots、ros联合调教自定义控制器实现
  10. error C2226: 语法错误 : 意外的“HFONT”类型