HTTP.Body
承載著HTTP.Message
昼弟,用于底層數(shù)據(jù)的傳輸蕾殴。這些數(shù)據(jù)可能是Json,html黄伊,文本或者圖片泪酱。
public enum Body {
case data(Bytes)
case chunked((ChunkStream) throws -> Void)
}
Data Case
data
是HTTP.Message
中body
最常用的,它是簡單的字節(jié)數(shù)組还最,一系列與之相關(guān)的類型或協(xié)議都定義在header
的Content-Type
中墓阀。下面來看一些??。
Application/JSON
如果我們的Content-Type
中包含application/json
憋活,那表明底層數(shù)據(jù)是序列化的JSON數(shù)據(jù)岂津。
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
,那表明底層數(shù)據(jù)時(shí)編碼的png圖片
if let contentType = req.headers["Content-Type"], contentType.contains("image/png"), let bytes = req.body.bytes {
try database.save(image: bytes)
}
Chunked Case
在vapor中chunked
僅使用與外部HTTP.Message
悦即,傳統(tǒng)的response角色是收集整個(gè)body然后將其傳遞吮成。我們可以利用代碼塊異步發(fā)送body。
let body: Body = Body.chunked(sender)
return Response(status: .ok, body: body)
我們可以手動(dòng)實(shí)現(xiàn)辜梳,或者使用Vapor內(nèi)置的初始方法快捷創(chuàng)建代碼塊body粱甫。
return Response(status: .ok) { chunker in
for name in ["joe", "pam", "cheryl"] {
sleep(1)
try chunker.send(name)
}
try chunker.close()
}
Note: 注意在chunker銷毀之前調(diào)用close()。
BodyRepresentable
除了常見的Body具體類型作瞄,Vapor還廣泛的支持BodyRepresentable
茶宵,這意味著對(duì)象可以轉(zhuǎn)換為Body
類型互換使用。比如:
return Response(body: "Hello, World!")
上例中字符串會(huì)轉(zhuǎn)換為字節(jié)添加到body中宗挥。
實(shí)際我們最好使用
return "Hello, World!"
乌庶,Vapor會(huì)自動(dòng)為Content-Type
設(shè)置適當(dāng)?shù)闹怠?/p>
看一下這是如何實(shí)現(xiàn)的:
public protocol BodyRepresentable {
func makeBody() -> Body
}
Custom
我們可以將自定義的類型遵守HTTP.BodyRepresentable
協(xié)議。如下例子中假設(shè)我們有一個(gè).vpr
文件契耿,可轉(zhuǎn)換為VPRFile
model瞒大,就可以作為body使用。
extension VPRFile: HTTP.BodyRepresentable {
func makeBody() -> Body {
// collect bytes
return .data(bytes)
}
}
你可能注意到了協(xié)議中是包含“thows”的搪桂,但是我們的實(shí)現(xiàn)中沒有透敌,這在swift中是完全可以的。這樣你在手動(dòng)調(diào)用該方法時(shí)就不用再拋出異常了。
然后我們就可以直接在Response
中使用VRP
文件了酗电。
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)
}
實(shí)際上魄藕,如果我們經(jīng)常重復(fù)這個(gè)操作,我們可能會(huì)將VPRFile
直接與ResponseRepresentable
配合使用:
extension VPRFile: HTTP.ResponseRepresentable {
func makeResponse() -> Response {
return Response(
status: .ok,
headers: ["Content-Type": "file/vpr"],
body: file
)
}
}
上面的例子也會(huì)改成這樣:
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)
}