一个简单的 HTTP 服务器可以用以下函数表示:
请求 => 响应
但是,我们通常需要执行有效的操作,例如从数据库中检索数据或调用外部服务。这种情况我们会这样定义:
请求 => F[响应]
http4s 对称为 HttpRoutes[F] 的路由(端点)有自己的抽象,它是文档中提到的 Kleisli[OptionT[F, ?], Request, Response] 的类型别名。
我们从 cat 文档中了解到,Kleisli 只是返回单子值的函数的组合,例如 Option[A]、Either[L, R] 等。
函数最有用的属性之一是它们可以组合。也就是说,给定一个函数 A => B 和一个函数 B => C,我们可以将它们组合起来创建一个新的函数 A => C。
在这种情况下,http4s 的 Kleislei 接受 Request 并返回 F[Response],只需 Request => F[Response]。 F 是一种效果类型。
对上面的 Request 和 Response 类型稍作修改,我们得到以下内容:
type HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]]
HttpRoutes[F] 被声明为一系列简单的 case 语句。每个 case 语句都尝试匹配并可选地从传入的 Request[F] 中提取。与第一个匹配案例关联的代码用于生成 F[Response[F]]。
最简单的 case 语句匹配所有请求而不提取任何内容。请求的右侧必须返回 F[Response[F]]。
我们可以在这里看到 F[Response[F]] 的简单演示
scala> val getRoot = Request[IO](Method.GET, uri"/")
getRoot: org.http4s.Request[cats.effect.IO] = Request(method=GET, uri=/, headers=Headers())
scala> val io = service.orNotFound.run(getRoot)
io: cats.effect.IO[org.http4s.Response[cats.effect.IO]] = <function1>
这里cats.effect.IO[org.http4s.Response[cats.effect.IO]] 尚未解决,这是异步结果。它将在将来的某个时候以简单的 Future/Promise 解决。我们可以强制它运行:
scala> val response = io.unsafeRunSync
response: org.http4s.Response[cats.effect.IO] = Response(status=200, headers=Headers())
- Kleisli
- Http4s
- Gabriel Volpe 的 Scala 实用 FP