在 Controller 中为每个 RequestMapping 配置不同的 FAIL_ON_UNKNOWN_PROPERTIES
Configure FAIL_ON_UNKNOWN_PROPERTIES for each RequestMapping differently in the Controller
我想在我的控制器中以不同的方式处理 json 到对象的不同 @RequestMapping
转换。
我相信如果我们在 spring-boot 项目中添加 Jackson 依赖项,它会处理 json 到对象的转换,并且 #spring.jackson.deserialization.fail-on-unknown-properties=true
属性 将确保转换失败如果 json 中存在一些未知的 属性 (如果我错了,请纠正我)。
我们能否在本地告诉 jackson 何时在未知属性上失败以及何时忽略那些 属性.
以下是使用标志的代码片段。
@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;
}
如果可以为每个映射添加一些验证之王,那么我不需要更改所有现有映射来自定义未知 属性 并且代码更改将最少。
Jackson
的 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
class 只有一个 属性 - 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
它 returns SUCCESS
值。当我们在 http://localhost:8080/options/success
URL
上发送上述两个 payload 时,app 在这两种情况下都是 returns SUCCESS
value.
另请参阅:
- How to access plain json body in Spring rest controller?
- Spring Boot: Customize the Jackson ObjectMapper
我能够通过实现我自己的 HttpMessageConverter 来达到预期的结果。感谢 @MichalZiober 的建议。
我创建了一个自定义 HttpMessageConvertor 并将其注册到我的自定义 MediaType:{"application", "json-failFast"}
。
这是如何工作的,只要 Header: Content-Type:application/json-failFast
存在,那么 @RequestBody/@ResponseBody
中的未知属性将不会被接受从 json 转换为 Object 和 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);
}
}
}
@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);
}
我想在我的控制器中以不同的方式处理 json 到对象的不同 @RequestMapping
转换。
我相信如果我们在 spring-boot 项目中添加 Jackson 依赖项,它会处理 json 到对象的转换,并且 #spring.jackson.deserialization.fail-on-unknown-properties=true
属性 将确保转换失败如果 json 中存在一些未知的 属性 (如果我错了,请纠正我)。
我们能否在本地告诉 jackson 何时在未知属性上失败以及何时忽略那些 属性.
以下是使用标志的代码片段。
@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;
}
如果可以为每个映射添加一些验证之王,那么我不需要更改所有现有映射来自定义未知 属性 并且代码更改将最少。
Jackson
的 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
class 只有一个 属性 - 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
它 returns SUCCESS
值。当我们在 http://localhost:8080/options/success
URL
上发送上述两个 payload 时,app 在这两种情况下都是 returns SUCCESS
value.
另请参阅:
- How to access plain json body in Spring rest controller?
- Spring Boot: Customize the Jackson ObjectMapper
我能够通过实现我自己的 HttpMessageConverter 来达到预期的结果。感谢 @MichalZiober 的建议。
我创建了一个自定义 HttpMessageConvertor 并将其注册到我的自定义 MediaType:{"application", "json-failFast"}
。
这是如何工作的,只要 Header: Content-Type:application/json-failFast
存在,那么 @RequestBody/@ResponseBody
中的未知属性将不会被接受从 json 转换为 Object 和 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);
}
}
}
@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);
}