轉載請附原文鏈接:http://blog.fandong.me/2017/08/20/iOS-SwiftVaporWeb23/
前言
之前一直有做Java后臺開發的興趣,可是想到要看好多的Java教程,作為一個iOS開發者,我放棄了,
后來從朋友韓云智VL那里知道了這個框架,竟是用Swift寫的,不得不說,它燃起了我的興趣。
Vapor是一個基于Swift開發的服務端框架,可以工作于iOS,Mac OS,Ubuntu。
為了配合Swift部署到服務器,我把ECS的服務器系統改為Ubuntu16.04。
請求體
HTTP.Body
表示的是HTTP.Message
的有效載荷,并用于傳遞底層數據,在這次練習中的一些例子是JSON
,HTML
文本或者二進制圖像,我們來看一下具體實現
public enum Body {
case data(Bytes)
case chunked((ChunkStream) throws -> Void)
}
數據案例
Data Case
是一個目前最常見的Body
中的HTTP.Message
.它只是一個字節數組,與這些字節數組關聯的序列化協議或類型通常由Content-Type
請求頭定義,我們來看些例子
Application/JSON
如果我們的Content-Type
請求頭包含application/json
,那么底層二級制數據表示序列化的JSON
if let contentType = req.headers["Content-Type"], contentType.contains("application/json"), let bytes = req.body.bytes {
let json = try JSON(bytes: bytes)
print("Got JSON: \(json)")
}
Image/PNG
如果我們的Content-Type
包含image/png
,則底層二進制數據表示編碼的png.
if let contentType = req.headers["Content-Type"], contentType.contains("image/png"), let bytes = req.body.bytes {
try database.save(image: bytes)
}
分塊案例
分塊案例只適用于Vapor的外發的HTTP.Message
,傳統意義上,響應者的角色是在傳遞之前收集整個分塊編碼,我們可以使用它來異步發送一個正文.
let body: Body = Body.chunked(sender)
return Response(status: .ok, body: body)
我們也可以手動實現,也可以使用Vapor的內置便利的初始化器來進行對請求體進行分塊.
return Response(status: .ok) { chunker in
for name in ["joe", "pam", "cheryl"] {
sleep(1)
try chunker.send(name)
}
try chunker.close()
}
確保在分塊離開范圍之前調用
close()
BodyRepressentable
除了具體的Body
類型,就像在Vapor中常見,我們也廣泛的支持BodyRepresentable
.這意味著我們通常轉換的類型的對象可以互換使用,舉個栗子
return Response(body: "Hello, World!")
在上面的例子中,字符串被轉換成二進制,并且添加到了請求體中
作為練習,最好使用
Hello,World!
Vapor會自動設置Content-Type
為合適的值
我們來看看它是如何實現的:
public protocol BodyRepresentable {
func makeBody() -> Body
}
自定義
我們也可以在適用的情況下遵照我們自己的類型,假設我們有一個自定義的數據類型,.vpr
讓我們符合我們的VPR
文件類型
extension VPRFile: HTTP.BodyRepresentable {
func makeBody() -> Body {
// collect bytes
return .data(bytes)
}
}
你可能已經注意到,協議拋出,但是我們沒有實現,這在Swift中完全有效,如果你曾經手動調用過這個方法,那你將無法拋出.
現在我們可以直接在我們的Responses
中包含我們的VPR
文件.
drop.get("files", ":file-name") { request in
let filename = try request.parameters.extract("file-name") as String
let file = VPRFileManager.fetch(filename)
return Response(status: .ok, headers: ["Content-Type": "file/vpr"], body: file)
}
作為練習,如果我們經常重復這些過程,我們可能會讓VPRFile
直接遵循ResponseRepresentable
extension VPRFile: HTTP.ResponseRepresentable {
func makeResponse() -> Response {
return Response(
status: .ok,
headers: ["Content-Type": "file/vpr"],
body: file
)
}
}
這是我們上面的例子
drop.get("files", ":file-name") { request in
let filename = try request.parameters.extract("file-name") as String
return VPRFileManager.fetch(filename)
}
我們也可以使用類型安全的路由使其更簡潔:
drop.get("files", String.self) { request, filename in
return VPRFileManager.fetch(filename)
}