将 `Option[A]` 转换为 Http4s 中的 Ok() 或 NotFound() API
Converting an `Option[A]` to an Ok() or NotFound() inside an Http4s API
我有一个 API 看起来像这样:
object Comics {
...
def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
...
以及如下所示的路由:
object Routes {
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap {
case Some(author) => Ok(author)
case None => NotFound()
}
所以当有 None
时,它会转换为 404。由于有多个路由,.flatMap { ... }
会重复。
问题:如何将它移动到一个单独的 .orNotFound
特定于我的项目的辅助函数中?
我的尝试:
为了让事情对我来说简单(并避免在 F
最初进行参数化),我尝试在 comicsRoutes
:
中定义它
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A]): ???[A] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound)
但是这里 ???
是什么?它似乎不是 Response
或 Status
。此外,.flatMap { ... }
是在 import dsl._
下创建的,但我想将它移到更远的地方。什么是好地方?它是进入路由文件,还是我把它放在 separate ExtendedSomething
extension file 中? (我预计 ???
和 Something
可能相关,但我对缺少的类型有些困惑。)
(同样重要的是,我如何找出这里的 ???
?我希望 ???
在类型级别可能会给我一个“打字洞”,并且 VSCode的悬停功能提供了非常零星的文档价值。)
Http4sDsl[F]
为您的操作返回的类型是 F[Response[F]]
。
它必须用 F
包裹,因为您在 F
上使用 .flatMap
。 Response
使用 F
进行参数化,因为它将生成使用 F
.
返回给调用方的结果
要找出答案,您可以使用 IntelliJ,然后通过 IDE 生成注释(Alt+Enter,然后“将类型注释添加到值定义”)。您还可以:
- preview implicits 检查从
Statuses
trait 导入的 Ok
对象是否提供了具有 http4sOkSyntax
隐式转换的扩展方法(Ctrl+Alt+Shift+加号,你可以按它多次扩展隐式,然后 Ctrl+Alt+Shift+Minut 再次隐藏它们)
- 通过按两次 Shift 打开查找 window 找到
http4sOkSyntax
,然后再按两次以包括非项目符号,
- 从那里使用 Ctrl+B 导航,通过
OkOps
到 EntityResponseGenerator
class,这为您提供了
您使用的功能(在 apply
中)返回 F[Resposne[F]]
.
因此,如果您想移动 around/extract 它们,请注意实例化 DSL 和扩展方法需要哪些隐式。
(快捷方式在 Mac OS 之间有所不同 - 有时使用 Cmd 而不是 Ctrl - 和非 Mac OS 系统所以如果你只是在文档中检查它们有问题)。
感谢 Mateusz,我了解到 ???
应该是 F[Response[F]]
。
为了使这个辅助函数充分发挥作用,又发生了两个与类型相关的问题:
因为 value: A
是多态的,Http4s 需要一个隐式的 EntityEncoder[F, A]
来序列化一个任意值。 (这不是原始 { case ... }
匹配的问题,因为类型是具体的而不是多态的。
出于某种原因,添加此隐式注释是不够的。做 .flatMap(orNotFound)
类型推断失败。 .flatMap(orNotFound[Authors.Slug])
解决了这个问题。
(感谢 keynmol 指出另外两个。)
完成所有三个更改后,结果为:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A])(implicit ee: EntityEncoder[F, A]): F[Response[F]] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound[Authors.Author])
...
我有一个 API 看起来像这样:
object Comics {
...
def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
...
以及如下所示的路由:
object Routes {
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap {
case Some(author) => Ok(author)
case None => NotFound()
}
所以当有 None
时,它会转换为 404。由于有多个路由,.flatMap { ... }
会重复。
问题:如何将它移动到一个单独的 .orNotFound
特定于我的项目的辅助函数中?
我的尝试:
为了让事情对我来说简单(并避免在 F
最初进行参数化),我尝试在 comicsRoutes
:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A]): ???[A] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound)
但是这里 ???
是什么?它似乎不是 Response
或 Status
。此外,.flatMap { ... }
是在 import dsl._
下创建的,但我想将它移到更远的地方。什么是好地方?它是进入路由文件,还是我把它放在 separate ExtendedSomething
extension file 中? (我预计 ???
和 Something
可能相关,但我对缺少的类型有些困惑。)
(同样重要的是,我如何找出这里的 ???
?我希望 ???
在类型级别可能会给我一个“打字洞”,并且 VSCode的悬停功能提供了非常零星的文档价值。)
Http4sDsl[F]
为您的操作返回的类型是 F[Response[F]]
。
它必须用 F
包裹,因为您在 F
上使用 .flatMap
。 Response
使用 F
进行参数化,因为它将生成使用 F
.
要找出答案,您可以使用 IntelliJ,然后通过 IDE 生成注释(Alt+Enter,然后“将类型注释添加到值定义”)。您还可以:
- preview implicits 检查从
Statuses
trait 导入的Ok
对象是否提供了具有http4sOkSyntax
隐式转换的扩展方法(Ctrl+Alt+Shift+加号,你可以按它多次扩展隐式,然后 Ctrl+Alt+Shift+Minut 再次隐藏它们) - 通过按两次 Shift 打开查找 window 找到
http4sOkSyntax
,然后再按两次以包括非项目符号, - 从那里使用 Ctrl+B 导航,通过
OkOps
到EntityResponseGenerator
class,这为您提供了 您使用的功能(在apply
中)返回F[Resposne[F]]
.
因此,如果您想移动 around/extract 它们,请注意实例化 DSL 和扩展方法需要哪些隐式。
(快捷方式在 Mac OS 之间有所不同 - 有时使用 Cmd 而不是 Ctrl - 和非 Mac OS 系统所以如果你只是在文档中检查它们有问题)。
感谢 Mateusz,我了解到 ???
应该是 F[Response[F]]
。
为了使这个辅助函数充分发挥作用,又发生了两个与类型相关的问题:
因为
value: A
是多态的,Http4s 需要一个隐式的EntityEncoder[F, A]
来序列化一个任意值。 (这不是原始{ case ... }
匹配的问题,因为类型是具体的而不是多态的。出于某种原因,添加此隐式注释是不够的。做
.flatMap(orNotFound)
类型推断失败。.flatMap(orNotFound[Authors.Slug])
解决了这个问题。
(感谢 keynmol 指出另外两个。)
完成所有三个更改后,结果为:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A])(implicit ee: EntityEncoder[F, A]): F[Response[F]] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound[Authors.Author])
...