如何将HandlerAspect的CtxOut对象传递至下游Handler?
在ZIO-HTTP中传递HandlerAspect的CtxOut值到下游Handler
核心问题:如何将ZIO-HTTP中HandlerAspect生成的BearerToken(CtxOut)传递给下游Handler?之前尝试ZIO.service[BearerToken]、ZIO.serviceWith[BearerToken]、zio.http.withContext均失败,因为BearerToken是动态生成的字符串包装类,无法通过静态Layer提供;也不想将其作为Endpoint输入(等同于弃用Middleware)。
最简解决方案
问题出在错误地使用ZIO.service获取BearerToken——ZIO.service用于从ZLayer中获取静态服务,而HandlerAspect注入的CtxOut属于Handler的动态环境,应通过ZIO.access或ZIO.environment获取。
修改要点
- 替换
ZIO.service[BearerToken]为ZIO.access[BearerToken](_.value),从Handler环境中直接提取动态生成的BearerToken。 - 修正Middleware中
BearerToken的构造逻辑,去掉Authorization header中的"Bearer "前缀。
修改后的完整代码
import zio.{ ZIO, ZIOAppDefault, ZLayer } import zio.http.{ string, Handler, HandlerAspect, Request, Response, RoutePattern, Routes, Server } import zio.http.endpoint.Endpoint import zio.schema.DeriveSchema object CarShop extends ZIOAppDefault { // 数据模型 case class Car(brand: String) object Car { implicit val schema = DeriveSchema.gen[Car] } // 服务层 object CarService { val layer: ZLayer[Any, Nothing, CarService] = ZLayer.succeed(CarService("Renault")) } case class CarService(brand: String) { def listCar: List[Car] = List( Car("Toyota"), Car("Honda"), ) def getCar(brand: String): Car = Car(s"$brand (${this.brand})") } // Endpoints定义 val listCarsEndpoint = Endpoint(RoutePattern.GET / "cars") .out[List[Car]] def listCarsHandler: Handler[CarService, Nothing, Unit, List[Car]] = Handler.fromFunctionZIO { _: Unit => ZIO.serviceWith[CarService](_.listCar) } val getCarEndpoint = Endpoint(RoutePattern.GET / "car" / string("brand")) .out[Car] // 修正后的Handler:从环境中获取BearerToken def getCarHandler: Handler[CarService with BearerToken, Nothing, String, Car] = Handler.fromFunctionZIO { brand: String => for { carService <- ZIO.service[CarService] bearer <- ZIO.access[BearerToken](_.value) // 正确获取动态注入的BearerToken } yield carService.getCar(s"$brand (${bearer.value})") } case class BearerToken(value: String) // 修正后的Middleware:去掉Bearer前缀 object BearerAuthMiddleware { def handler: HandlerAspect[Any, BearerToken] = HandlerAspect.interceptIncomingHandler { Handler.fromFunctionZIO[Request] { req => req.headers.get("Authorization") match { case Some(auth) if auth.startsWith("Bearer ") => ZIO.succeed((req, BearerToken(auth.drop(7)))) // 去掉"Bearer "前缀 case _ => ZIO.fail(Response.unauthorized) } } } } val routes: Routes[CarService with BearerToken, Nothing] = Routes( listCarsEndpoint.implementHandler(listCarsHandler), getCarEndpoint.implementHandler(getCarHandler), ) @@ BearerAuthMiddleware.handler def run = Server .serve(routes) .provide( CarService.layer, Server.default, ) }
对应的build.sbt
name := "car-shop" libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.1.18", "dev.zio" %% "zio-http" % "3.3.3" )
内容的提问来源于stack exchange,提问作者Biketire
相关产品推荐
相关产品推荐

