Search code examples
jsonspring-bootjacksondeserializationspring-restcontroller

Configure FAIL_ON_UNKNOWN_PROPERTIES for each RequestMapping differently in the Controller


I want to handle json to Object conversion differently on different @RequestMapping in my Controller.

I believe if we add Jackson dependency in our spring-boot project it handles json to Object conversion and #spring.jackson.deserialization.fail-on-unknown-properties=true property will make sure that conversion will fail if there is some unknown property present in the json (please correct me if I am wrong).

Can we tell jackson locally when to fail on unknown properties and when to ignore those property.

Following is code snippet to use a flag.

    @GetMapping(value = "sample")
    public @ResponseBody UserDTO test(@RequestParam String str, @RequestParam boolean failFast) {
        ObjectMapper map = new ObjectMapper();
        if( failFast) {
            map.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
        } else {
            map.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
        UserDTO userDTO = null;
        try {
            userDTO = map.readValue(str, UserDTO.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userDTO;
    }

I don't need it to be handled at runtime like i am doing using @RequestParam. Is there some property using which i can use to mark mappings where to check for unknown properties and where to ignore them.

Edit: What I am looking for is to change an existing application to handle Unknown property per mapping. For example:

        @PostMapping(value = "fail/fast")
        public @ResponseBody UserDTO test(@FAIL_ON_UNKNOWN @RequestBody UserDTO userDTO, @RequestParam boolean failFast) {
            ..///processing...
            return userDTO;
        }

        @PostMapping(value = "fail/safe")
        public @ResponseBody UserDTO test( @RequestBody UserDTO userDTO, @RequestParam boolean failFast) {
                ..///processing...
                return userDTO;
        }

If some king of validation can be added per mapping then i don't need to change all existing mapping's to customise unknown property and code change will be minimum.


Solution

  • I was able to achieve the desired result by implementing my own HttpMessageConverter. Thanks to @MichalZiober for suggesting it.

    I created a Custom HttpMessageConvertor and registered it with my custom MediaType:{"application", "json-failFast"}.

    How this works is whenever Header: Content-Type:application/json-failFast is present then unknown properties in @RequestBody/@ResponseBody will not be accepted while converting from json to Object and UnrecognizedPropertyException will be thrown.

    And whenever Header: Content-Type:application/json is present then unrecognised properties in @RequestBody/ResponseBody will be ignored.

    Here is my custom HttpMessageConverter:

    @Component
    public class CustomJsonMessageConverter extends AbstractJackson2HttpMessageConverter {
    
        @Nullable
        private String jsonPrefix;
    
        public CustomJsonMessageConverter() {
            this(Jackson2ObjectMapperBuilder.json().build().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,true));
        }
        public CustomJsonMessageConverter(ObjectMapper objectMapper) {
            super(objectMapper, new MediaType[]{ new MediaType("application", "json-failFast")});
        }
    
        public void setJsonPrefix(String jsonPrefix) {
            this.jsonPrefix = jsonPrefix;
        }
    
        public void setPrefixJson(boolean prefixJson) {
            this.jsonPrefix = prefixJson ? ")]}', " : null;
        }
    
        protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
                if (this.jsonPrefix != null) {
                generator.writeRaw(this.jsonPrefix);
            }
        }
    }