在Controller中为每个RequestMapping配置FAIL_ON_UNKNOWN_PROPERTIES

时间:2019-10-08 17:24:55

标签: json spring-boot jackson deserialization spring-restcontroller

我想在控制器中的不同@RequestMapping上以不同的方式处理json到Object的转换。

我相信,如果我们在spring-boot项目中添加Jackson依赖项,它将处理json到Object的转换,并且#spring.jackson.deserialization.fail-on-unknown-properties=true属性将确保如果json中存在某些未知的属性,转换将失败(请纠正我如果我错了)。

我们能否在本地告诉杰克逊何时在未知属性上失败以及何时忽略那些属性。

以下是使用标记的代码段。

    @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;
    }

我不需要像运行@RequestParam.那样在运行时进行处理 是否可以使用某些属性来标记映射,以便在哪里检查未知属性以及在哪里忽略它们。

修改: 我正在寻找的是更改现有应用程序以处理每个映射的未知属性。例如:

        @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;
        }

如果可以为每个映射添加一些验证之王,那么我不需要更改所有现有映射来自定义未知属性,并且代码更改将最少。

3 个答案:

答案 0 :(得分:1)

Jackson的{​​{1}}允许您使用自定义配置创建新的ObjectMapper。您可以在应用中创建一个常见的ObjectReader实例,并且对于某些控制器,该实例用作创建自定义阅读器的基础对象。它将允许您使用所有常用功能和已注册的模块,并在需要时进行更改。参见下面的控制器:

ObjectMapper

import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.Objects; @RestController @RequestMapping(value = "/options") public class JacksonOptionsController { private final ObjectMapper objectMapper; @Autowired public JacksonOptionsController(ObjectMapper objectMapper) { this.objectMapper = Objects.requireNonNull(objectMapper); } @PostMapping(path = "/fail") public ResponseEntity<String> readAndFastFail(HttpServletRequest request) throws IOException { String json = readAsRawJSON(request); Payload payload = createFailFastReader().readValue(json); return ResponseEntity.ok("SUCCESS"); } @PostMapping(path = "/success") public ResponseEntity<String> readAndIgnore(HttpServletRequest request) throws IOException { String json = readAsRawJSON(request); Payload payload = createSafeReader().readValue(json); return ResponseEntity.ok("SUCCESS"); } private ObjectReader createFailFastReader() { return objectMapper .readerFor(Payload.class) .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } private ObjectReader createSafeReader() { return objectMapper .readerFor(Payload.class); } private String readAsRawJSON(HttpServletRequest request) throws IOException { try (InputStreamReader reader = new InputStreamReader(request.getInputStream())) { try (StringWriter out = new StringWriter(64)) { reader.transferTo(out); return out.toString(); } } } } 类只有一个属性-Payload。在一个控制器中,我们使用id和启用的ObjectReader。在其他情况下,我们将DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES与默认配置一起使用,而ObjectReader被禁用。

对于测试请求:

DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES

应用引发异常并请求:

curl -i -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"id":"some-value","id1":1}' http://localhost:8080/options/fail

它返回curl -i -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"id":"some-value"}' http://localhost:8080/options/fail 值。当我们在SUCCESS http://localhost:8080/options/success上发送两个以上的有效载荷时,两种情况下的应用都返回URL值。

另请参阅:

答案 1 :(得分:1)

我能够通过实现自己的 HttpMessageConverter 来获得所需的结果。感谢 @MichalZiober 的建议。

我创建了一个自定义HttpMessageConvertor并将其注册到我的自定义MediaType:{"application", "json-failFast"}

只要存在 Header: Content-Type:application/json-failFast ,这是如何工作的,那么在将json转换为Object和<时, @RequestBody/@ResponseBody 中的未知属性将不被接受strong> UnrecognizedPropertyException 将被抛出。

并且只要存在 Header: Content-Type:application/json ,就会忽略 @RequestBody/ResponseBody 中无法识别的属性。

这是我的自定义 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);
        }
    }
}

答案 2 :(得分:1)

@Autowired
private RequestMappingHandlerAdapter converter;

@Override
public void afterPropertiesSet() throws Exception {
    configureJacksonToFailOnUnknownProperties();
}

private void configureJacksonToFailOnUnknownProperties() {
    MappingJackson2HttpMessageConverter httpMessageConverter = converter.getMessageConverters().stream()
            .filter(mc -> mc.getClass().equals(MappingJackson2HttpMessageConverter.class))
            .map(mc -> (MappingJackson2HttpMessageConverter)mc)
            .findFirst()
            .get();

    httpMessageConverter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}