Currently, I have a REST service (user-service) running with:

Java 17
Spring Boot: 3.1.4
 -> Jackson library: 2.15.2
 -> Spring Framework: 6.0.12
Spring Cloud: 2022.0.4

I have the next POST endpoint to log in to the app via REST:

@RequestMapping(path = "/v3/auth/login", method = RequestMethod.POST, consumes = Constants.CONSUMES_JSON)
public ResponseEntity login(@RequestBody LoginRequestBody body) {
    return this.loginLogic(body);
}

The LoginRequestBody object has the following structure:

public class LoginRequestBody {
    @Nullable
    public Email email;

    @Nullable
    public String password;

    public LoginRequestBody() {}

    @JsonCreator
    public LoginRequestBody(
        @JsonProperty(value = "email", required = false) @Nullable Email email,
        @JsonProperty(value = "password", required = false) @Nullable  String password) {
        this.email = email;
        this.password = password;
    }
}

@EqualsAndHashCode
public class Email {
    private String email;

    @JsonCreator
    public Email(@JsonUnwrapped @Nonnull String email) {
        Assert.notNull(email, "email == null");
        EmailValidator.assertIsValid(email);
        this.email = email;
    }

    @JsonUnwrapped
    @Nonnull
    public String getEmail() {
        return email;
    }

    public String toString() {
        return getEmail();
    }

    @Override
    public int hashCode() {
        return email.hashCode();
    }
    public static Optional<Email> of(@Nullable String email) {
        Email instance = EmailValidator.isValid(email) ? new Email(email) : null;
        return Optional.ofNullable(instance);
    }

}

When I request via POSTMAN this POST endpoint, it returns 200 and the expected token.

POST /v3/auth/login HTTP/1.1
Host: localhost:8181
Content-Type: application/json
Content-Length: 68

{
    "email":"test@test.io",
    "password":"test"
}

This was working pretty well until I have updated to:

Java 17
Spring Boot: 3.2.3
 -> Jackson library: 2.15.4
 -> Spring Framework: 6.1.4
Spring Cloud: 2023.0.0

Now when I make the same request, it returns a 415 error:

{
    "timestamp": 1710949005118,
    "status": 415,
    "error": "Unsupported Media Type",
    "path": "/v3/auth/login"
}

And the application logs prints:

2024-03-20T16:36:45.109+01:00  WARN 14717 --- [user-service] [  XNIO-1 task-2] [65fb028d936f634df64e07a7ca16fc8b-d98152c051b5f4f6] ustomMappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.p.a.user.api.request.LoginRequestBody]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property "email" as `@JsonUnwrapped`: combination not yet supported
2024-03-20T16:36:45.112+01:00  WARN 14717 --- [user-service] [  XNIO-1 task-2] [65fb028d936f634df64e07a7ca16fc8b-d98152c051b5f4f6] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.p.a.user.api.request.LoginRequestBody]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property "email" as `@JsonUnwrapped`: combination not yet supported
2024-03-20T16:36:45.115+01:00  WARN 14717 --- [user-service] [  XNIO-1 task-2] [65fb028d936f634df64e07a7ca16fc8b-d98152c051b5f4f6] ustomMappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.p.a.user.api.request.LoginRequestBody]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property "email" as `@JsonUnwrapped`: combination not yet supported
20

I tested the Jackson library (2.15.4) in both scenarios and the result is the same. Also, I know Spring Framework has been updated and I added the <parameters>true</parameters> configuration to the maven-compiler-plugin.

Something had to change in how Spring handles the POST requests as it is not able to handle the email field.

PS: As a workaround, I have created a deserialiser for the Email and it works fine but this does not explain why the behaviour has changed.

Thank you

Comment From: bclozel

Thanks for reaching out. I think this is similar to https://github.com/spring-projects/spring-framework/issues/31959 - the only difference between the two Spring Boot versions is the registration of the parameter name module in Spring Framework. This module was already registered in the Spring Boot JSON codec in 3.1.x

With 3.1.x, the first codec instance fails with Failed to evaluate Jackson deserialization for type [[simple type, class com.example.unsupported.LoginRequestBody]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property "email" as@JsonUnwrapped: combination not yet supported and the second works because it does not have this module registered.

With 3.2.x, both codecs fail the same way.

I think there is a way to reproduce this problem without Spring being involved at all. You can choose to work on such setup to report this behavior difference, or only fix the invalid setup in your Email class which was broken in the first place.

I'm closing this issue here as Spring Boot has nothing to do with this and the module registration change in Spring Framework has already been documented.

Thanks!