3

当两者结合时,http4s如何优先考虑有什么规则吗AuthedRoutesHttpRoutes<+>

因为我收到了 401 这样的组合authRoute <+> route,所以在调用时:

GET /non-authed-route

行为发生了变化,当我重新排序路由时,我收到了 200 个相同的请求route <+> authRoute

当您需要使用 5 个以上需要组合的路由并且其中一些可能同时包含常规和安全端点时,它可能会更加混乱。

4

1 回答 1

8

因此,尽管该问题缺少一些上下文信息或代码,但我将假设您的问题与常见问题足够相似:

首先,我假设您的代码具有这种近似形式,并且是用一个AuthedRoutes.of(定义在这里)和一个AuthMiddleware

    val moonAuthedRoutes: AuthedRoutes[F, Int] = AuthedRoutes.of {
      case GET -> / sky / moon as T(42)
    }
    val marsRoutes: HttpRoutes[F] = HttpRoutes.of {
      case GET -> / sky / mars
    }
    // assuming you have your authedMiddleware 
    val moonRoutes = authedMiddleware(moonAuthedRoutes)
    val composedRoutes = moonRoutes <+> marsRoutes

现在,如果您了解所涉及的别名的类型,以及打开包装后它们归结为什么,它会有所帮助KleisliOptionT

type HttpRoutes[F, T] 
  = Kleisli[OptionT[F, ?], Request[F, T], Response[F]]
  ~~ Request[F] => F[Option[Response[F]]

type AuthedRoutes[F, T] 
  = Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]]
  ~~ (T, Request[F]) => F[Option[Response[F]]

本质上,一个函数接受 HTTP 请求和一些身份验证上下文,并可能给出计算一些响应或没有响应(在 中的计算中F)。要将其转换AuthedRoutesHttpRoutes,您使用身份验证中间件:您的包装器AuthedRoutes接受请求,尝试从中提取请求T,如果成功,则使用该请求包装请求T并将其传递给AuthedRoutes,如这行代码中所做的那样. 如果中间件无法验证请求(即提取T),则从这一行开始,它会短路并返回401 Unauthenticated响应。

现在,<+>组合首先应用于moonRoutes. 如果结果是一些响应,那就是组合路由返回的结果。只有当路由moonRoutes(在 的左侧<+>)返回 anF(None)时,组合才会将请求传递给marsRoutes。特别是,当您给它一个未经身份验证的请求时,它会moonRoutes返回一些 401 Unauthenticated响应,这就是composedRoutes返回的内容,而无需将请求传递给marsRoutes.

但是,如果您交换顺序并写入composedRoutes = marsRoutes <+? moonRoutes,则对未经身份验证的端点的未经身份验证的请求将在marsRoutes; 而经过身份验证的请求首先在 中尝试marsRoutes,它失败(给出一个F(None)),然后传递给moonRoutes,它成功。

于 2020-03-28T19:50:09.263 回答