FrancMo
FrancMo

Reputation: 2699

Http4s decoder how to customize error message for invalid fields

I have following code like:

 case req @ POST -> Root =>
        req
          .decode[UserCreateRequest] { decodedRequest =>

my stack is http4s + zio.

Ive added custom decoder for this case class where I have a line:

email <- Either.cond(StringValidator.isValidEmail(emailStr), Email(emailStr), DecodingFailure("email", c.history))

Posting invalid json, with invalid email returns me:

HTTP/1.1 422 Unprocessable Entity Content-Type: text/plain; charset=UTF-8 Date: Tue, 19 Jan 2021 16:46:27 GMT Content-Length: 29

The request body was invalid.

Response code: 422 (Unprocessable Entity); Time: 681ms; Content length: 29 bytes

which I would like to customize. In http4s code I see InvalidMessageBodyFailure. But I can not find in docs any info how to customize this response.

Any one maybe tried this already ?

thanks

edit:

sample UserCreateRequest:

final case class UserCreateRequest(
    email: Email
  )

final case class Email(value: String) extends AnyVal

json request:

{
 "email": "myemail[at]gmail.com"
}

this can be achieved using such code:

(for {
          decodedJson <- req.asJson.mapError { decodingError =>
            HttpDecodingError(cause = decodingError.getMessage)
          }
          decodedRequest <- Task.fromEither(decodedJson.as[UserCreateRequest]).mapError { decodingError =>
            HttpDecodingError(cause = decodingError.getMessage)
          }
          response <- UserService
            .createNewUser(
              decodedRequest.email
            )
            .bimap(
              error => HttpGenericError(msg = error.msg, cause = error.cause.toString),
              u => UserResponse(u.email.value)
            )
        } yield response).foldM((error: HttpError) => BadRequest(error), u => Ok(u))

but I wonder if it can be simplified, by some http4s core features, which are done already but not documented :)

Upvotes: 2

Views: 1191

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149558

You can directly return the Status from your API. i.e. you could construct an UnprocessableEntity instance and use the withXXX methods to alter the response.

Assuming some structure:

final case class UserCreateRequest(isValid: Boolean)

You can do:

case req @ POST -> Root / "foo" =>
  for {
    req <- req.decodeJson[UserCreateRequest]
    resp <- if (req.isValid) Ok()
            else UnprocessableEntity().map(_.withEntity(???).withAttribute(???))
  } yield resp

Upvotes: 2

Related Questions