HTTP.Body承载着HTTP.Message,用于底层数据的传输。这些数据可能是Json,html,文本或者图片。
public enum Body {
case data(Bytes)
case chunked((ChunkStream) throws -> Void)
}
Data Case
data是HTTP.Message中body最常用的,它是简单的字节数组,一系列与之相关的类型或协议都定义在header的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)
}
Chunked Case
在vapor中chunked仅使用与外部HTTP.Message,传统的response角色是收集整个body然后将其传递。我们可以利用代码块异步发送body。
let body: Body = Body.chunked(sender)
return Response(status: .ok, body: body)
我们可以手动实现,或者使用Vapor内置的初始方法快捷创建代码块body。
return Response(status: .ok) { chunker in
for name in ["joe", "pam", "cheryl"] {
sleep(1)
try chunker.send(name)
}
try chunker.close()
}
Note: 注意在chunker销毁之前调用close()。
BodyRepresentable
除了常见的Body具体类型,Vapor还广泛的支持BodyRepresentable,这意味着对象可以转换为Body类型互换使用。比如:
return Response(body: "Hello, World!")
上例中字符串会转换为字节添加到body中。
实际我们最好使用
return "Hello, World!",Vapor会自动为Content-Type设置适当的值。
看一下这是如何实现的:
public protocol BodyRepresentable {
func makeBody() -> Body
}
Custom
我们可以将自定义的类型遵守HTTP.BodyRepresentable协议。如下例子中假设我们有一个.vpr文件,可转换为VPRFilemodel,就可以作为body使用。
extension VPRFile: HTTP.BodyRepresentable {
func makeBody() -> Body {
// collect bytes
return .data(bytes)
}
}
你可能注意到了协议中是包含“thows”的,但是我们的实现中没有,这在swift中是完全可以的。这样你在手动调用该方法时就不用再抛出异常了。
然后我们就可以直接在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)
}
实际上,如果我们经常重复这个操作,我们可能会将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)
}






网友评论