2

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

FrancMo
  • 2,459
  • 2
  • 19
  • 39

1 Answers1

2

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
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • on first look it seemed to be ok, but my problem is that inside UserCreateRequest I have value classes, and it fails inside this decode method :( – FrancMo Jan 20 '21 at 07:49
  • @Baku I think that's another matter, separate from the question you asked here (how do I encode more information inside a response). Perhaps a new question would be more suitable. – Yuval Itzchakov Jan 20 '21 at 07:52
  • but why ? Ive asked for that in main question ;) I showed how my decoder looks like. Maybe it was not clear your at the beginning. do you know maybe how to solve this ? thanks – FrancMo Jan 20 '21 at 07:57
  • @Baku Your question says you would like to customize the response, it fails to mention anything about value class decoding, which is what you've stated here. – Yuval Itzchakov Jan 20 '21 at 07:59
  • but Ive added line with: "email <- Either.cond(StringValidator.isValidEmail(emailStr), Email(emailStr), DecodingFailure("email", c.history))" if it was not clear I can edit question then. what is the problem I dont understand – FrancMo Jan 20 '21 at 08:01
  • Question contains information that Ive custom decoder – FrancMo Jan 20 '21 at 08:03
  • @Baku Can you elaborate exactly what doesn't work? What is the error message? – Yuval Itzchakov Jan 20 '21 at 08:17
  • its in ticket description ;) "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 " – FrancMo Jan 20 '21 at 08:20
  • I I can do smth like: https://gist.github.com/maciejbak85/249a3331d08fecb203f1864f77a36180, but I wonder if it could be done easier, more automated, probably I will have to make some custom methods, coz http4s probably does not provide it – FrancMo Jan 20 '21 at 08:23
  • @Baku Can you also post the structure of the `UserCreateRequest` value class? – Yuval Itzchakov Jan 20 '21 at 08:25
  • BTW request body was invalid might indicate there's an error with the payload you're sending rather than the handler. Can you also post a sample JSON that's failing? – Yuval Itzchakov Jan 20 '21 at 08:26
  • well in json when I put: "email": "myemail[at]gmail.com", – FrancMo Jan 20 '21 at 09:10