简体   繁体   中英

How to add custom error responses in Http4s?

Whenever I hit unknown route in my http4s application it returns 404 error page with Content-Type: text/plain and body:

Not found

How can I force it to always return body as JSON with Content-Type: application/json ?

{"message": "Not found"}

I figured out that when I assembly httpApp I can map over it and "adjust" responses:

val httpApp = Router.publicRoutes[F].orNotFound.map(ErrorTranslator.handle)

where ErrorTranslator just detects responses with status code of client error and Content-Type which is not application/json and then just wraps body into 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
  }

}

It works, but I wonder if there is maybe some less convoluted solution?

It would be also great if a solution could "translate" other errors, like 400 Bad request into JSON, similarily to presented code.

I suppose you have defined your routes in a similar fashion, then you can add a default case statement

 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"}"""))
}

You can also make it with value and mapF function:

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))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM