文档

§基本操作

§什么是基本操作?

基本操作是 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 过滤器


发现此文档中的错误?此页面的源代码可以在 此处 找到。阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?前往 我们的社区论坛 与社区开始对话。