如何在 Http4s 中添加自定义错误响应?
How to add custom error responses in Http4s?
每当我在我的 http4s 应用程序中遇到未知路由时,它 returns 404 错误页面 Content-Type: text/plain 和 body:
Not found
如何强制它始终 return 正文为 JSON 且 Content-Type: application/json?
{"message": "Not found"}
我发现当我组装 httpApp 时我可以映射它并且 "adjust" 响应:
val httpApp = Router.publicRoutes[F].orNotFound.map(ErrorTranslator.handle)
其中 ErrorTranslator
仅检测具有客户端错误状态代码和 Content-Type 的响应,而不是 application/json 然后将 body 包装成 JSON:
object ErrorTranslator {
val ContentType = "Content-Type"
val ApplicationJson = "application/json"
private def translate[F[_]: ConcurrentEffect: Sync](r: Response[F]): Response[F] =
r.headers.get(CaseInsensitiveString(ContentType)).map(_.value) match {
case Some(ApplicationJson) => r
case _ => r.withEntity(r.bodyAsText.map(ErrorView(_))) //wrap reponse body into enity
}
def handle[F[_]: ConcurrentEffect: Sync]: PartialFunction[Response[F], Response[F]] = {
case Status.ClientError(r) => translate(r)
case r => r
}
}
有效,但我想知道是否有一些不那么复杂的解决方案?
如果解决方案可以 "translate" 其他错误,如 400 Bad request into JSON,这也很好,类似于提供的代码。
我想你已经以类似的方式定义了你的路由,那么你可以添加一个 default case 语句
HttpRoutes.of[IO] {
case GET -> Root / "api" =>
Ok()
case _ -> Root =>
// Your default route could be done like this
Ok(io.circe.parser.parse("""{"message": "Not Found"}"""))
}
你也可以用值和mapF
函数来实现:
val jsonNotFound: Response[F] =
Response(
Status.NotFound,
body = Stream("""{"error": "Not found"}""").through(utf8Encode),
headers = Headers(`Content-Type`(MediaType.application.json) :: Nil)
)
val routes: HttpRoutes[F] = routes.someRoutes().mapF(_.getOrElse(jsonNotFound))
每当我在我的 http4s 应用程序中遇到未知路由时,它 returns 404 错误页面 Content-Type: text/plain 和 body:
Not found
如何强制它始终 return 正文为 JSON 且 Content-Type: application/json?
{"message": "Not found"}
我发现当我组装 httpApp 时我可以映射它并且 "adjust" 响应:
val httpApp = Router.publicRoutes[F].orNotFound.map(ErrorTranslator.handle)
其中 ErrorTranslator
仅检测具有客户端错误状态代码和 Content-Type 的响应,而不是 application/json 然后将 body 包装成 JSON:
object ErrorTranslator {
val ContentType = "Content-Type"
val ApplicationJson = "application/json"
private def translate[F[_]: ConcurrentEffect: Sync](r: Response[F]): Response[F] =
r.headers.get(CaseInsensitiveString(ContentType)).map(_.value) match {
case Some(ApplicationJson) => r
case _ => r.withEntity(r.bodyAsText.map(ErrorView(_))) //wrap reponse body into enity
}
def handle[F[_]: ConcurrentEffect: Sync]: PartialFunction[Response[F], Response[F]] = {
case Status.ClientError(r) => translate(r)
case r => r
}
}
有效,但我想知道是否有一些不那么复杂的解决方案?
如果解决方案可以 "translate" 其他错误,如 400 Bad request into JSON,这也很好,类似于提供的代码。
我想你已经以类似的方式定义了你的路由,那么你可以添加一个 default case 语句
HttpRoutes.of[IO] {
case GET -> Root / "api" =>
Ok()
case _ -> Root =>
// Your default route could be done like this
Ok(io.circe.parser.parse("""{"message": "Not Found"}"""))
}
你也可以用值和mapF
函数来实现:
val jsonNotFound: Response[F] =
Response(
Status.NotFound,
body = Stream("""{"error": "Not found"}""").through(utf8Encode),
headers = Headers(`Content-Type`(MediaType.application.json) :: Nil)
)
val routes: HttpRoutes[F] = routes.someRoutes().mapF(_.getOrElse(jsonNotFound))