Vapor文档学习廿九: HTTP -Body

HTTP.Body承载着HTTP.Message,用于底层数据的传输。这些数据可能是Json,html,文本或者图片。

public enum Body {
    case data(Bytes)
    case chunked((ChunkStream) throws -> Void)
}

Data Case

dataHTTP.Messagebody最常用的,它是简单的字节数组,一系列与之相关的类型或协议都定义在headerContent-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)
}

你可能感兴趣的:(Vapor文档学习廿九: HTTP -Body)