Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

时间:2024-03-18 16:57:50

上节我们介绍了阿里云ECS上部署个Swift Perfect的实例环境的方法,下面我将继续介绍如何实际开发一个简单的服务器接口并与mysql数据库进行交互;

三、依托阿里云服务器,搭建一个Perfect+mysql的服务

1.运用swift包管理工具建立一个干净的Perfect工程

首先我们建立一个文件夹这里我们可以直接用FileZilla创建个文件夹,或者用命令mkdir创建个文件夹比如我建的文件夹叫WebService ;执行mkdir WebService 我们得到个空文件夹WebService;

 cd WebService

SPM软件包管理器初始化项目:

swift package init --type=executable


这个命令会在当前工作目录自动生成下列文件:


Creating executable package: WebService

Creating Package.swift

Creating README.html

Creating .gitignore

Creating Sources/

Creating Sources/WebService/main.swift

Creating Tests/


若需要使用git来管理工程,则我们可以对.gitignore进行编辑来忽略一些不必要同步的文件,下面是一个通用的忽略文件


# Created by https://www.gitignore.io/api/swift


### Swift ###

# Xcode

#

# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore


## Build generated

build/

DerivedData/


## Various settings

*.pbxuser

!default.pbxuser

*.mode1v3

!default.mode1v3

*.mode2v3

!default.mode2v3

*.perspectivev3

!default.perspectivev3

xcuserdata/


## Other

*.moved-aside

*.xccheckout

*.xcscmblueprint


## Obj-C/Swift specific

*.hmap

*.ipa

*.dSYM.zip

*.dSYM


## Playgrounds

timeline.xctimeline

playground.xcworkspace


# Swift Package Manager

#

# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.

# Packages/

# Package.pins

.build/


# CocoaPods - Refactored to standalone file


# Carthage - Refactored to standalone file


# fastlane

#

# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the

# screenshots whenever they are needed.

# For more information about the recommended setup visit:

# https://docs.fastlane.tools/best-practices/source-control/#source-control


fastlane/report.xml

fastlane/Preview.html

fastlane/screenshots

fastlane/test_output


#xcode文件

*.xcodeproj


# End of https://www.gitignore.io/api/swift


将上面内容复制到.gitignore文件后即可通过git来同步服务端与本地的代码,由于服务端的代码修改比较麻烦,没有本地的Xcode简单,所以建议使用Git来管理代码,当这个空白的基础工程建好后,我们就可以通过服务端的git 来上传这个空白项目;为啥要上传空白项目?因为我们后面的编程将在Mac本地使用Xcode来完成;我们使用 

git add *  


git commit -m 描述提交的内容 

git push --set-upstream  origin  master

命令来上传项目内容;

若第一次配置git可能无法提交,所以第一次我们需要配置git

git init 命令初始化git配置

git remote add origin {git地址}命令来添加git地址


git上传完成后,随后我们就可以在本地Mac上同步git clone  {git地址}下载这个工程;



工程下载完毕,我们在本地的终端上cd到工程目录,然后执行

swift package generate-xcodeproj

命令,这样我们就会得到一个WebService.xcodeproj工程文件,Xcode打开这个工程文件,我们就可以看到我们这个Swift空白项目了;

首先我们要修改Package.swift文件,这个是Perfect框架的引用包;

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)


import PackageDescription

let package = Package(

name: "WebService",

targets: [],

dependencies: [

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

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

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

.Package(url: "https://github.com/SwiftORM/MySQL-StORM.git", majorVersion: 3),

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

]

)

修改完成后在终端上运行swift build

这一过程是漫长而枯燥的,耐心等待,如果出现问题请重试
第一类错误:

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

这个错误就说明,你的Package文件里配置有问题,请按照我上面说的配置,或者去点开上面的git连接,去Perfect项目官方地址上去找配置设置路径,当然当你的代码里出现编译错误时也是可能会出现这个问题的,当Xcode编译不通过时,请先解决Xcode里的编辑错误,先保证Xcode可以编译通过在来swift build我们现在还没有写逻辑代码,所以基本不会出现这个问题。

第二类错误

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

找到链接里对应的文件,删了,重新build

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

swift 的项目编译文件都在WebService项目文件夹的.build文件夹里,这个文件夹是个隐藏文件夹,我们可以打开WebService文件夹按下command+shift+.组合按键来显示这个文件夹,当出现编译不通过出现某些框架无法下载时也可以删除这个文件夹来重新swift build

2.安装mysql


当然由于我们使用了mysql,所以在最后一步编译出错很正常,我们要在Mac中安装mysql,安装mysql的方法不在赘述,详细可参考https://www.jianshu.com/p/fd3aae701db9

安装完mysql

1,检查是否安装了mysql
commond+shift+G
输入/usr/local回车看看有没有把

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

如果和笔者的一样,那你已经安装了,如果没有,那请去官网下载连接是直接到下载页面的https://dev.mysql.com/downloads/mysql/下载安装2,使用navcat或者mysqlworkbench等其它工具连接数据库,笔者用的mysqlworkbench点击左上角mysqlworkbench->Database->connect to Database来连接数据库起个名字作为WebService ,port默认,userName和password输入mysql创建时设置的密码,若改了密码就输入改后的,重要的一点事Encoding;

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

编码格式是UTF-8下面有一个选项是默认勾选的,它有什么作用呢?如果你勾选上了,你会发现你存的汉字都是乱码,所以这里不勾选它。3,连接时提示你root密码错误这里笔者也是遇到了,mac下安装时,没有提示设置密码,笔者当时安装时好像是这样的,这时你要重置密码http://jingyan.baidu.com/article/63f236280a11680208ab3d91.html连接放上面了,具体我不赘述了

再次执行swift package generate-xcodeproj

这个时候可以用Xcode打开运行了

运行错误解决方案 

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

解决方案参考文章http://www.jianshu.com/p/c117294e2442

错误一 

Header '/usr/local/include/mysql/mysql.h' 

这个错误是因为我们存放mysql.h文件的路径和它引用的路径不同。用 Homebrew安装的MySQL路径确实是正确的。 

解决方法: 

点击Finder,选择前往文件夹,进入/usr/local目录下,你会发现有mysql文件夹,在文件夹里找到对应mysql.h得到目录,将报错的module.modulemap文件中的路径修改成你自己的路径就可以了。这里我自己最后的路径是/usr/local/mysql-5.7.15-osx10.11-x86_64/include/mysql.h 

错误二 

ld: library not found for -lmysqlclient for architecture x86_64 

解决方法: 

在Target中找到MySQL,找到Library Search Paths中加上mysql文件夹下的lib的文件夹路径。我的是/usr/local/Cellar/mysql/5.7.16/lib

在Target中找到PerfectTemplate找到Other Linker Flags 加上-L/usr/local/Cellar/mysql/5.7.16/lib其中在lib目录下能找到对应的mysqlclient文件。 

当所有错误都解决的时候,你可以运行了,当然这个时候逻辑是空的,所以不会有什么效果,下面我们要为Perfect编写代码逻辑,我们在Source/WebService文件夹下建立个Service文件夹用来放我们的业务逻辑代码,并建立一个NetworkServer.swift文件

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

添加如下代码

import PerfectHTTP

import PerfectHTTPServer

import PerfectRequestLogger


open class NetworkServer {

    

    let server = HTTPServer();

    let httplogger = RequestLogger();

    let requestFilter = RequestFilter();

    let responseFilter = ResponseFilter();

    

    var routes : Routes = Routes.init(baseUri: "/v1");

    

    init(serverName:String,serverPort:UInt16) {

        server.serverPort = serverPort;

        server.serverName=serverName;

        self.initRoutes();

        server.addRoutes(routes);

        server.documentRoot="./webroot";

        server.setRequestFilters([(httplogger, .high),(RequestFilter(), .low)]);

        server.setResponseFilters([(ResponseFilter(), .high),(httplogger, .low)]); 

    }

    

    func start() -> Void {

        do {

            try server.start();

        } catch {

            fatalError("\(error)"); // fatal error launching one of the servers

        }

    }

    

    func initRoutes() -> Void {

        routes.add(Route.init(methods: [.get,.post], uri: "/**", handler: handler));

    }

    

    func handler(request: HTTPRequest, response: HTTPResponse) {

      response.appendBody(string:” hellow world" );

      response.completed();

    }

    

    struct RequestFilter : HTTPRequestFilter {

        func filter(request: HTTPRequest, response: HTTPResponse, callback: (HTTPRequestFilterResult) -> ()) {

            request.setHeader(.contentType, value: "text/html;charset=UTF-8");

            callback(.continue(request, response))

        }

    }

    

    struct ResponseFilter : HTTPResponseFilter {

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

            response.setHeader(.contentType, value: "text/html;charset=UTF-8");

            callback(.continue);

        }

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

            callback(.continue);

        }

    }

}

在main.swift中我们加上这样一句

let server = NetworkServer.init(serverName: "localhost", serverPort: 8181);

server.start();

这样我们的本地服务器就可以启动了

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

当log 窗口出现如图显示log时表明服务器已经启动,有可能会出现不能识别NetworkServer类的情况这时候我们需要在Xcode里添加这个文件的引用;

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

现在我们已经建立了个Perfect的工程并成功启动了服务,

现在我们将建立个实例来使用MySQLStORM来进行数据库访问与对象转换;

1.设计表结构

在mysql里添加个数据库WebService 并且添加个表结构User

Swift Perfect 从零开始到阿里云ECS实际应用教程(三)

这里要注意下主键必须用id字段并且放在第一位,因为后面StORM会按一定的规则使用这个其他方式我还没有试,暂且使用这个方式吧,按上图的方式创建这个表并点击Apply;

在代码main.swift中加入

import MySQLStORM

MySQLConnector.host = "localhost";

MySQLConnector.username = “root”;//数据库用户名

MySQLConnector.password = “000000”;//数据库密码

MySQLConnector.port = 3306;//数据库端口

MySQLConnector.database = “WebService”;//数据库名

这样我们的程序就连接上mysql数据库了;

下面我们创建个数据Model类



import StORM


class UserRegisterModel : MySQLStORM {

    var id : Int = 0;//这个id对应数据表结构里的主键,必须放到第一个

    var userName : String = "";

    var password : String = "";

    var loginTime : String = "";

    var registerTime : String = "";

    

    // The name of the database table

    override open func table() -> String { return "User" }//这个User就是要操作的表名

    

    override func to(_ this: StORMRow) {

        if this.data["id"] is Int {

            id = this.data["id"] as! Int;

        }

        else if this.data["id"] != nil {

            let id32 = this.data["id"] as! Int32;

            id = Int(id32);

        }//这里有个坑,StORM在转化的时候id会返回Int32类型,但很多方法使用的时候都只能转成Int类型,所以这里处理成转换为Int类型,若有更好的方法可提出来

        userName = this.data["userName"] as! String;

        password = this.data["password"] as! String;

        loginTime = this.data["loginTime"] as! String;

        registerTime = this.data["registerTime"] as! String;

    }

    

    func rows() -> [UserRegisterModel] {

        var rows = [UserRegisterModel]()

        for i in 0..<self.results.rows.count {

            let row = UserRegisterModel()

            row.to(self.results.rows[i])

            rows.append(row)

        }

        return rows

    }

}


添加个class


class NetRequest {

    

}


class UserRegister: NetRequest {

    

    public static func requestHandler(request: HTTPRequest, response: HTTPResponse) {

        // need to make sure something is available.

        

        let userName :String! = request.param(name: "userName");

        let password :String! = request.param(name: "password");

        

        let user = UserRegisterModel();

        

        user.userName=userName;

        user.password=password;

       

        let registerTime : String = try! formatDate(getNow(), format: "%Y-%m-%d %H:%M:%S”);//这里有个方法可以处理时间,swift Perfect 对foundation框架有很多坑,这里都是用Darwin和SwiftGlibc

        user.registerTime = registerTime;

        try? user.find([("userName", userName)]);

        

        guard user.results.foundSetCount <= 0 else {

//判断说明 该用户已存在

 response.sendData(data: ["code":100,"message":"用户名不存在"]);

                     return;

        }

        

        try? user.save(set: { (id) in

            user.id=id as! Int;//存储user数据到数据库里,并返回主键id,这里就有坑,若是id设置成int32类型,这里将不在走

        })

        

        guard user.results.insertedID > 0  else {

//判断说明 注册是否成功(数据是否被插入数据库)

 response.sendData(data: [“code”:100,"message":"注册失败"]);

            return;

        }

response.sendData(data: [“code”:200,”message”:"注册成功",data:user.asDataDict()]);

    }

}





下面代码是将Model转成json的代码





extension HTTPResponse {

func sendData(data : Any) -> Void {

        var encodedString : String! = "";

        do {

            encodedString = try data.jsonEncodedString();

        }

        catch{

            

        }

        self.appendBody(string: encodedString);

        self.completed();

    }

}


 最后我们把这些代码与文件加入到工程里并在

NetworkServer.swift文件里的initRoutes方法修改为

 func initRoutes() -> Void {

        routes.add(Route.init(methods: [.get,.post], uri: "/**", handler: handler));

        routes.add(Route.init(methods: [.get,.post], uri: "/api/register", handler: UserRegister.requestHandler));

    }

这样我们的一个注册接口就完成了。


启动服务, 在浏览器上调用http://localhost:8181/v1/api/register?userName=test&password=123456,这样我们将会看到返回的json数据了

{ code200, data: 
{ userName: 
"test", loginTime: "2018-05-08 09:48:00", id6, registerTime: "2018-05-08 09:48:00" }, message"success"

}

可能会有不同,这里不做计较。这时我们本地的一个接口服务就算调试完成了,下节我们要将服务部署到服务器上。

下面附上源码地址https://github.com/z123sen/perfectServer

下一篇 https://blog.csdn.net/z123sen/article/details/80248667