Kyeiv
Kyeiv

Reputation: 73

How to not accept additional fields in incoming JSON request body

I have a Spring Boot server that listens on endpoint. I accept @RequestBody as an object:

class Body {
   private String name;
}

I want it to accept requests like:

{
   "name": "some_name"
}

However, it also accepts:

{
   "name": "some_name",
   "dummy key":"dummy key value"
}

In that case I want it to throw error. How can I achieve it?

Upvotes: 4

Views: 4085

Answers (3)

13ibrahimov
13ibrahimov

Reputation: 1730

Configuring JSON request body deserialization rules in Spring

When configuring Spring's handling of unknown properties (fields) in JSON request body, the following are used in combination:

  1. Global level property (application.properties):

    spring.jackson.deserialization.fail-on-unknown-properties=true/false
    
  2. Class level annotation (respective entity class):

    @JsonIgnoreProperties(ignoreUnknown=false/true)
    

1. Global property (spring.jackson.deserialization.fail-on-unknown-properties)

If set to true, any field(s) in the JSON payload that do not have a corresponding field in the Java entity class, will throw an exception.

For instance, a POST request to your endpoint with the following JSON body will result in client getting the response - 400 BAD REQUEST:

{
   "name": "some_name",
   "dummy key":"dummy key value"
}

If set to false (default behaviour), any extra fields are simply ignored.

2. @JsonIgnoreProperties annotation (over entity class)

  • ignoreUnknown=false: An exception will be thrown if there are any fields in the JSON payload that do not correspond to fields in the Java class.
  • ignoreUnknown=true: Extra fields are simply ignored.

Problem (default case)

Logically you should be able to set the global property in application.properties and use @JsonIgnoreProperties annotation to override this for classes you want to make an exception.

However, as of Spring 4.1, the default global property is set to false (was set to true in older versions) and it cannot be overridden with @JsonIgnoreProperties(ignoreUnknown = false) at the class level.

This means that Spring will continue ignoring extra fields in JSON request body even if you annotate the class with @JsonIgnoreProperties(ignoreUnknown=false) unless you configure the global default behaviour.

Solution

Option 1:

  • Explicitly set spring.jackson.deserialization.fail-on-unknown-properties=true in application.properties.
  • For entity classes where you don't want to enforce this behavior, annotate them with @JsonIgnoreProperties(ignoreUnknown=true).
  • Remember that vice-versa doesn't work, which was the problem.

Option 2:

  • In your @Configuration class, add the following bean:
    @Bean
    public Jackson2ObjectMapperBuilder mapperBuilder() {
        return new Jackson2ObjectMapperBuilder().failOnUnknownProperties(true);
    }
    
  • This brings back the old behavior, equivalent to setting spring.jackson.deserialization.fail-on-unknown-properties=true.
  • And you can then simply override this in entity classes if necessary via @JsonIgnoreProperties(ignoreUnknown=true).

Upvotes: 1

Sandeep Kumar Nat
Sandeep Kumar Nat

Reputation: 478

You can do this in the controller when saving:

@PostMapping("/add")
public ResponseEntity<Body> registerUser(@Valid @RequestBody Body saveUser) {
    Body createdUser = userService.save(saveUser);
    
    return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}

When Spring finds an argument annotated with @Valid, it automatically validates the argument and throws an exception if the validation fails.

Or you can add this in the application.properties:

spring.jackson.deserialization.fail-on-unknown-properties=true

This helps us to make deserialization fail on unknown properties and throw an exception which we can handle using handleHttpMessageNotReadable.

Create controller advice to handle exceptions:

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(
            HttpMessageNotReadableException ex, HttpHeaders headers,
            HttpStatus status, WebRequest request) {
        return new ResponseEntity("Your Response Object", 
                HttpStatus.INTERNAL_SERVER_ERROR);
    } 
}

Upvotes: 2

Arturo Mendes
Arturo Mendes

Reputation: 738

TL;DR;

If you want the controller to return an error when the RequestBody does not match what you are expecting it is enough to add this to your application.properties:

spring.jackson.deserialization.fail-on-unknown-properties=true

This will return a default 400 response on malformed requests.

The @Valid annotation

Adding the @Valid annotation to your controller performs constraint validation and it will only fail when, for example, you mark a field as to be of a maximum value of 1 but the request tries to set it to 2. As per the docs:

Marks a property, method parameter or method return type for validation cascading. Constraints defined on the object and its properties are validated when the property, method parameter or method return type is validated.

But since you are not using any of the tools provided by jakarta.validation this annotation will have no effect on your code.

Upvotes: 2

Related Questions