簡體   English   中英

將 `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助手 function 中?


我的嘗試:

為了使事情對我來說簡單(並避免最初對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)

但是是什么??? 這里? 它似乎不是ResponseStatus 此外, .flatMap {... }是在import dsl._下制作的,但我想把它移到更遠的地方。 什么是好地方? 將它 go 放入路由文件中,還是將其放入單獨的ExtendedSomething擴展文件中? (我希望???Something可能相關,但我對缺少的類型有點困惑。)

(同樣重要的是,我如何找出???在這里?我希望???在類型級別上可能會給我一個“類型漏洞”,而 VSCode 的 hover function 提供了非常零星的文檔價值。)

Http4sDsl[F]為您的操作返回的類型是F[Response[F]]

它必須用F包裝,因為您在F上使用.flatMap Response使用F參數化,因為它將產生使用F返回給調用者的結果。

要找出這一點,您可以使用 IntelliJ,然后通過 IDE 生成注釋(Alt+Enter,然后“將類型注釋添加到值定義”)。 你也可以:

  • 預覽隱式檢查從Statuses trait 導入的Ok object 是否提供了具有http4sOkSyntax隱式轉換的擴展方法(Ctrl+Alt+Shift+加號,您可以按幾次以擴展隱式更多,Ctrl+Alt+Shift+Minut 到再次隱藏它們)
  • 通過按兩次Shift打開找到http4sOkSyntax找到window,然后再次按兩次以包含非項目符號,
  • 從那里使用 Ctrl+B 通過OkOps導航到EntityResponseGenerator class ,它為您提供您使用的功能(在apply中)返回F[Resposne[F]]

因此,如果您想移動/提取它們,請注意實例化 DSL 和擴展方法所需的隱式。

(Mac OS 之間的快捷方式不同 - 有時使用 Cmd 而不是 Ctrl - 和非 Mac OS 系統,因此如果您有問題,只需在文檔中檢查它們)。

感謝Mateusz,我知道了??? 應該是F[Response[F]]

為了讓這個助手 function 充分工作,又出現了兩個與類型相關的問題:

  1. 由於value: A是多態的,Http4s 需要一個隱式的EntityEncoder[F, A]來序列化任意值。 (這不是原始{ case... }匹配的問題,因為類型是具體的而不是多態的。

  2. 由於某種原因,添加這個隱式注釋是不夠的。 執行.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])
      ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM