基於Vapor3框架的Swift項目實戰《NewsBox》-(二:用戶註冊模塊)
創建新項目
首先我們創建一個新的空工程
在終端輸入
vapor new NewsBox
然後cd到創建好的目錄下
cd NewsBox
生成xcode項目並使用xcode打開
vapor xcode -y
第一次使用這個命令可能會等待swift包管理器自動下載相關依賴
完成後xcode就會自動打開該項目
下面是創建好的項目結構和相關依賴庫版本
現在主流的註冊方式爲手機號+驗證碼或郵箱+驗證碼,由於手機驗證碼要收費,對於本demo來說就採用郵箱加驗證碼的方式
郵箱+驗證碼實現註冊
使用郵箱服務有幾種方式
1. 在自己的服務器上面搭建郵箱服務器
2. 在其他地方註冊一個郵箱並開啓SMTP服務,在自己的服務器上面通過SMTP協議來發送郵件
當然對於本demo來說搭建郵箱服務器不是重點,所以就採用第二種方式
需要一個支持SMTP的郵箱賬戶,具體開啓SMTP服務的方法依據不同的郵箱服務商不同,自行百度
在項目的Package.swift
中添加SMTP相關依賴庫
本demo使用MySQL,因此也一併講相關庫加進去
我的Package文件內容如下:
name: "PUBGNewsBox",
dependencies: [
// �� A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
// ���� Swift ORM (queries, models, relations, etc) built on MySQL.
.package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.0-rc"),
/// mail Server
.package(url: "https://github.com/IBM-Swift/Swift-SMTP.git", from: "4.0.1")
],
targets: [
.target(name: "App", dependencies: ["SwiftSMTP", "FluentMySQL", "Vapor"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
重新在終端裏面用
vapor xcode -y
打開一次
接下來就是難點了
怎麼去讓服務器響應我們的請求
服務器怎麼返回結果參考新建項目后里面的ToDo開頭的文件
OK看完了之後回到這裏,我們直接上代碼
新建一個Swift文件名字:Email 內容如下
import Foundation
import Vapor
import SwiftSMTP
struct PUBGBoxEmail : Content{
var email : String
var verifycode : String?
}
fileprivate var emailDic = Dictionary<String,String>()
/// Configure mail Server
fileprivate let smtp = SMTP(hostname: "郵箱SMTP服務地址", // SMTP server address
email: "這裏填你的郵箱地址", // username to login
password: "密碼") // password to login
fileprivate let PUBGNewsBoxMailAddress = Mail.User(name: "發件人名稱", email: "發件人郵箱")
/// check verify code
extension PUBGBoxEmail{
func checkVerifyCode() ->Bool{
guard let verifyCode = emailDic[self.email] else {
return false
}
guard let customVerifyCode = self.verifycode else{
return false
}
if verifyCode == customVerifyCode {
emailDic[self.email] = nil
return true
}
return false
}
}
extension PUBGBoxEmail{
static func sendEmailCode(_ email : String ) -> Bool{
var verifyCode = emailDic[email]
if verifyCode == nil{
verifyCode = RandomString.sharedInstance.getRandomStringOfLength(length: 4)
}
///catch result and return
let userMail = Mail.User(email: email)
let mail = Mail(
from: PUBGNewsBoxMailAddress,
to: [userMail],
subject: "歡迎註冊PUBG新聞盒子",
text: "您本次註冊的驗證碼爲:\(verifyCode!)"
)
var result = true
smtp.send(mail) { (error) in
if let error = error {
print(error)
result = false
}else{
emailDic[email] = verifyCode!
}
}
return result
}
}
/// 隨機字符串生成
class RandomString {
let str = "1234567890"
/**
生成隨機字符串,
- parameter length: 生成的字符串的長度
- returns: 隨機生成的字符串
*/
func getRandomStringOfLength(length: Int) -> String {
var ranStr = ""
for _ in 0..<length {
let index = Int(arc4random_uniform(UInt32(str.count)))
ranStr.append(str[str.index(str.startIndex, offsetBy: index)])
}
return ranStr
}
private init() { }
static let sharedInstance = RandomString()
}
下面來講解
首先我們創建一個Dictonary來保存郵箱和對應的驗證碼
當收到一個發送驗證碼請求時,隨機產生4位數,然後發送郵件到收到的郵箱裏面去
如果發送成功,就緩存郵箱和對應的驗證碼,否則就返回錯誤給用戶(如郵箱地址錯誤等)—-(這裏有一點優化的地方,後面說)
這裏只實現了郵箱相關的方法,並沒有涉及到數據獲取與返回
接下來我們去接受用戶的請求
爲了便於管理,我們創建一個Controller
創建一個名爲UserController的Swift文件
下面是該文件的內容
import Foundation
import Vapor
import FluentMySQL
fileprivate let paramError = "請求參數錯誤"
fileprivate let secretKeyError = "App鑑權錯誤"
/// 1.檢查客戶端密鑰是否正確
/// ```
/// try SercretKey.JudgeSercretKey(sercreKey: requestBody.sercretkey,req: req).map(){result in
/// if !result {
/// return ResponseModel<String>(status: -3, message: secretKeyError, data: nil)
/// }
/// ```
/// 2.檢查用戶登陸狀態
///```
///let loginStatus = AccessToken.checkAccessToken(accessToken: requestBody.accessToken)
///if .ok != loginStatus {
/// return req.eventLoop.newSucceededFuture(result: ResponseModel<String>(status: -1, message: loginStatus.rawValue, data: nil))
///}
///```
final class UserController{
/// 用戶註冊
func register(req : Request)throws -> Future<ResponseModel<UserRegistInfo> >{
return try req.content.decode(RequestModel<UserRegistInfo>.self).flatMap(){ requestBody in
return try SercretKey.JudgeSercretKey(sercreKey: requestBody.sercretkey,req: req).flatMap(){result in
if !result {
return req.eventLoop.newSucceededFuture(result: ResponseModel<UserRegistInfo>(status: -3, message: secretKeyError, data: nil))
}
if requestBody.data == nil {
return req.eventLoop.newSucceededFuture(result: ResponseModel<UserRegistInfo>(status: -2, message: paramError, data: nil))
}
return try requestBody.data!.check(req: req).flatMap(){checkresult in
if checkresult == .ok {
requestBody.data!.password = try encryptPassword(password: requestBody.data!.password!)
return requestBody.data!.save(on: req).map(){ user in
return ResponseModel<UserRegistInfo>(status: 0, message: "註冊成功", data: nil)
}
}else{
return req.eventLoop.newSucceededFuture(result: ResponseModel<UserRegistInfo>(status: -1, message: checkresult.rawValue, data: nil))
}
}
}
}
}
}
上面說到的客戶端密鑰和用戶登陸狀態驗證先不管
主要看register函數
函數接受一個Request參數,返回一個Future<ResponseModel<UserRegistInfo> >
關於Future/Promise這裏不講,請不瞭解的自行百度
在傳進來的req裏面,我們解碼出一個RequestModel<UserRegistInfo>
這裏的ResponseModel和RequestModel內容如下
import Vapor
struct ResponseModel<T> : Content where T : Content{
var status : Int
var message : String
var data : T?
}
final class RequestModel<T> : Content where T : Codable{
var sercretkey : String
var timestamp : Int
var accessToken : String?
var data : T?
init(sercretkey : String, timestamp : Int) {
self.sercretkey = sercretkey
self.timestamp = timestamp
}
}
簡單來說就是使用了範型,在Vapor框架裏,想要把請求參數轉換成Model或者把Model轉換成返回參數(JSON)的話,只需要Model繼承Content就行了,這裏使用了Swift4的一個重要特性,SwiftNIO裏的Codable。
最後我們需要給我們的請求指定一個路徑
在routes中
let userRouter = router.grouped("user")
let userController = UserController()
userRouter.post("register", use: userController.register)
然後請求參數JSON如下:
{
"sercretkey":"123",
"timestamp":456132,
"data":
{
"username":"tzcccy2",
"password":"0054321",
"email":"****@qq.com",
"verifycode":"3432"
}
}
除去”sercretkey”:”123”,
“timestamp”:456132,
先不看
具體請查看github上的源碼
本文demo地址