§基本操作
§什么是基本操作?
基本操作是 Action[A] 的更简单的底层类型。要理解基本操作,我们需要了解 Play 架构。
Play 的核心非常小,周围环绕着大量有用的 API、服务和结构,使 Web 编程任务更轻松。
基本上,Play 的操作 API 抽象地具有以下类型
RequestHeader -> Array[Byte] -> Result
上面的 计算 接收请求头 RequestHeader
,然后接收请求体作为 Array[Byte]
并生成 Result
。
现在,这种类型假定将请求体完全放入内存(或磁盘),即使您只想从中计算一个值,或者更好地将其转发到像 Amazon S3 这样的存储服务。
我们更希望以流的形式接收请求体块,并在必要时能够逐步处理它们。
我们需要更改的是第二个箭头,使其以块的形式接收输入,并最终生成结果。有一种类型可以完全做到这一点,它被称为 Accumulator
,它接受两个类型参数。
Accumulator[E,R]
是一种 箭头 类型,它将以 E
类型的块的形式接收输入,并最终返回 R
。对于我们的 API,我们需要一个接收 ByteString
块(本质上是字节数组的更有效包装器)并最终返回 Result
的 Accumulator。因此,我们稍微修改了类型,使其成为
RequestHeader -> Accumulator[ByteString, Result]
对于第一个箭头,我们只是使用 Function[From, To],它可以用 =>
进行类型别名。
RequestHeader => Accumulator[ByteString, Result]
现在,如果我为 Accumulator[E,R]
定义一个中缀类型别名
type ==>[E,R] = Accumulator[E,R]
,那么我就可以用更有趣的方式编写类型。
RequestHeader => ByteString ==> Result
这应该读作:获取请求头,获取代表请求体的 ByteString
块,最终返回一个 Result
。这正是 EssentialAction
类型定义的方式。
trait EssentialAction extends (RequestHeader => Accumulator[ByteString, Result])
另一方面,Result
类型可以抽象地认为是响应头和响应体。
case class Result(header: ResponseHeader, body: ByteString)
但是,如果我们想逐步将响应体发送给客户端,而无需将其完全填充到内存中,该怎么办?我们需要改进我们的类型。我们需要将主体类型从 ByteString
替换为生成 ByteString
块的内容。
我们已经有一个类型可以做到这一点,它被称为 Source[E, _]
,这意味着它能够生成 E
块,在本例中为 Source[ByteString, _]
。
case class Result(header: ResponseHeader, body: Source[ByteString, _])
如果我们不需要逐步发送响应,我们仍然可以将整个主体作为单个块发送。在实际的 API 中,Play 使用 HttpEntity
包装类型支持不同类型的实体,该类型支持流式、分块和严格实体。
请注意,Play 的内置助手(如 Ok(myObject)
)使用隐式 Writeable[E]
实例将 myObject
转换为实体,该实例从实体 E
创建 HttpEntity
。
§底线
Play HTTP API 非常简单。
RequestHeader -> Accumulator[ByteString, Result]
或者更有趣的是
RequestHeader => ByteString ==> Result
这读作:获取 RequestHeader
,然后获取 ByteString
块,并返回一个响应。响应包含 ResponseHeaders
和一个主体,该主体是可转换为 ByteString
的值的块,用于写入 Source[E, _]
类型中表示的套接字。
下一步:HTTP 过滤器
发现此文档中的错误?此页面的源代码可以在 此处 找到。阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?前往 我们的社区论坛 与社区开始对话。