简体   繁体   中英

HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `int` from String

How to show conversion error along with other validation errors when creating REST API?

  • Spring Boot
  • Spring MVC
  • REST Controller
@Data
@AllArgsConstructor
class DTO
{
    @NotNull
    @Min(1)
    private Integer id;
}

@RestController
class ExampleController
{
    @PostMapping("/")
    public void createEndpoint(@Valid @RequestBody DTO dto) {
        return "{\"message\": \"OK\"}";
    }
}

When I make request to this endpoint,

POST /
{
    "id: "abrakadabra"
}

I would like to get something like this

{
    "errors": {
        "id": [
            { code: "invalid_format", message: "Field must contain only digits" }
        ]
    }
}

What I actually get?

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `int` from String "abrakadabra": not a valid `int` value;
nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `int` from String "abrakadabra": not a valid `int` value
at [Source: (PushbackInputStream); line: 2, column: 24] (through reference chain: com.mylid.back.dtos.CreateCompanyDto["pricing_plan_id"])]

I know that I can create custom annotation for validation. BUT the problem is that the process does not reach validation. It fails on deserialization step.

[Possible way]

There is one dirty trick, to change Integer type in DTO to String. Create custom annotation to check if String contains only digits. Then manually map this String from DTO to Integer in Entity. Then save to database.

What are other ways how to solve this problem? It is very weird, that there is a few topics on Google and StackOverFlow for this particular problem. And on almost every page the accepted answer is "it is not our problem, API clients should pass integer".

In PHP we can easily do it with 'integer' validator, almost every framework has it, and it will not prevent other fields from validation if I pass "abracadabra" instead of "1".

What about implementing a custom type?

For example (the code below is crap, missing null handling and so on - just wanted to show you the idea)

public class DTOId {
    final Integer value;

    private DTOId(String value) {
        Assert.hasText(value, "Value for DTOId should not not be null or empty!");
        // Validation that it is a integer, min value and so on
        this.value = Integer.valueOf(value);
    }

    @JsonCreator
    public static DTOId of(final String value) {
        return new DTOId(value);
    }

    @JsonValue
    public String toString() {
        return value.toString();
    }
}

Then you could use

@Data
@AllArgsConstructor
class DTO
{
    @NotNull
    private DTOId id;
}

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