我正在将一个应用程序从Spring Platform 1.1.3.RELEASE更新到2.0.1.RELEASE,它将Spring Framework版本从4.1.7升级到4.2.4,而Jackson从2.4.6升级到2.6.4。 Spring或Jackson处理自定义HttpMessageConverter
实现似乎没有任何重大更改,但我的自定义JSON序列化未能发生,我无法确定原因。以下版本在以前的Spring Platform版本中运行良好:
模型
@JsonFilter("fieldFilter")
public class MyModel {
/*model fields and methods*/
}
模型包装
public class ResponseEnvelope {
private Set<String> fieldSet;
private Set<String> exclude;
private Object entity;
public ResponseEnvelope(Object entity) {
this.entity = entity;
}
public ResponseEnvelope(Object entity, Set<String> fieldSet, Set<String> exclude) {
this.fieldSet = fieldSet;
this.exclude = exclude;
this.entity = entity;
}
public Object getEntity() {
return entity;
}
@JsonIgnore
public Set<String> getFieldSet() {
return fieldSet;
}
@JsonIgnore
public Set<String> getExclude() {
return exclude;
}
public void setExclude(Set<String> exclude) {
this.exclude = exclude;
}
public void setFieldSet(Set<String> fieldSet) {
this.fieldSet = fieldSet;
}
public void setFields(String fields) {
Set<String> fieldSet = new HashSet<String>();
if (fields != null) {
for (String field : fields.split(",")) {
fieldSet.add(field);
}
}
this.fieldSet = fieldSet;
}
}
控制器
@Controller
public class MyModelController {
@Autowired MyModelRepository myModelRepository;
@RequestMapping(value = "/model", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public HttpEntity find(@RequestParam(required=false) Set<String> fields, @RequestParam(required=false) Set<String> exclude){
List<MyModel> objects = myModelRepository.findAll();
ResponseEnvelope envelope = new ResponseEnvelope(objects, fields, exclude);
return new ResponseEntity<>(envelope, HttpStatus.OK);
}
}
自定义HttpMessageConverter
public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
private boolean prefixJson = false;
@Override
public void setPrefixJson(boolean prefixJson) {
this.prefixJson = prefixJson;
super.setPrefixJson(prefixJson);
}
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
ObjectMapper objectMapper = getObjectMapper();
JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody());
try {
if (this.prefixJson) {
jsonGenerator.writeRaw(")]}', ");
}
if (object instanceof ResponseEnvelope) {
ResponseEnvelope envelope = (ResponseEnvelope) object;
Object entity = envelope.getEntity();
Set<String> fieldSet = envelope.getFieldSet();
Set<String> exclude = envelope.getExclude();
FilterProvider filters = null;
if (fieldSet != null && !fieldSet.isEmpty()) {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet))
.setFailOnUnknownId(false);
} else if (exclude != null && !exclude.isEmpty()) {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept(exclude))
.setFailOnUnknownId(false);
} else {
filters = new SimpleFilterProvider()
.addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept())
.setFailOnUnknownId(false);
}
objectMapper.setFilterProvider(filters);
objectMapper.writeValue(jsonGenerator, entity);
} else if (object == null){
jsonGenerator.writeNull();
} else {
FilterProvider filters = new SimpleFilterProvider().setFailOnUnknownId(false);
objectMapper.setFilterProvider(filters);
objectMapper.writeValue(jsonGenerator, object);
}
} catch (JsonProcessingException e){
e.printStackTrace();
throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage());
}
}
}
配置
@Configuration
@EnableWebMvc
public class WebServicesConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FilteringJackson2HttpMessageConverter jsonConverter = new FilteringJackson2HttpMessageConverter();
jsonConverter.setSupportedMediaTypes(MediaTypes.APPLICATION_JSON);
converters.add(jsonConverter);
}
// Other configurations
}
现在我收到此异常(由Spring捕获并记录)并在发出任何类型的请求时出现500错误:
[main] WARN o.s.w.s.m.s.DefaultHandlerExceptionResolver - Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content:
Can not resolve PropertyFilter with id 'fieldFilter';
no FilterProvider configured (through reference chain:
org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0]);
nested exception is com.fasterxml.jackson.databind.JsonMappingException:
Can not resolve PropertyFilter with id 'fieldFilter';
no FilterProvider configured (through reference chain:
org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0])
configureMessageConverters
方法执行,但看起来不像在请求期间使用自定义转换器。是否有可能另一个消息转换器阻止此消息转发我的响应?我的理解是,覆盖configureMessageConverters
会阻止使用手动注册的转换器以外的转换器。
除了通过Spring Platform更新依赖版本之外,此代码的工作版本和非工作版本之间没有任何更改。我在文档中遗漏的JSON序列化是否有任何变化?
修改
进一步测试会产生奇怪的结果。我想测试以检查以下内容:
HttpMessageConverter
实际上是否已注册?所以,我添加了一个额外的测试并看了一下输出:
@Autowired WebApplicationContext webApplicationContext;
@Before
public void setup(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void test() throws Exception {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) webApplicationContext.getBean("requestMappingHandlerAdapter");
List<EntrezGene> genes = EntrezGene.createDummyData();
Set<String> exclude = new HashSet<>();
exclude.add("entrezGeneId");
ResponseEnvelope envelope = new ResponseEnvelope(genes, new HashSet<String>(), exclude);
for (HttpMessageConverter converter: adapter.getMessageConverters()){
System.out.println(converter.getClass().getName());
if (converter.canWrite(ResponseEnvelope.class, MediaType.APPLICATION_JSON)){
MockHttpOutputMessage message = new MockHttpOutputMessage();
converter.write((Object) envelope, MediaType.APPLICATION_JSON, message);
System.out.println(message.getBodyAsString());
}
}
}
......它运作正常。我的envelope
对象及其内容被序列化并正确过滤。因此,请求处理在到达消息转换器之前存在问题,或者MockMvc
测试请求的方式发生了变化。
答案 0 :(得分:10)
您的配置正常。不从自定义转换器调用writeInternal()
的原因是因为您覆盖了错误的方法。
查看4.2.4.RELEASE
<强> AbstractMessageConverterMethodProcessor#writeWithMessageConverters 强>
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
((GenericHttpMessageConverter<T>) messageConverter).write(returnValue, returnValueType, selectedMediaType, outputMessage);
...
}
<强> AbstractGenericHttpMessageConverter#写强>
public final void write(final T t, final Type type, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
...
writeInternal(t, type, outputMessage);
...
}
从writeInternal(...)
内调用的方法AbstractGenericHttpMessageConverter#write(...)
有三个参数 - (T t, Type type, HttpOutputMessage outputMessage)
。您正在覆盖只有2个参数的writeInternal(...)
的重载版本 - (T t, HttpOutputMessage outputMessage)
。
但是,在版本4.1.7.RELEASE
中,情况并非如此,因此是问题的根本原因。此版本中使用的writeInternal(...)
是您重写的另一个重载方法(具有2个参数的方法)。这解释了为什么它在4.1.7.RELEASE
中正常工作。
@Override
public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
...
writeInternal(t, outputMessage);
...
}
因此,要解决您的问题,而不是覆盖writeInternal(Object object, HttpOutputMessage outputMessage)
,请覆盖writeInternal(Object object, Type type, HttpOutputMessage outputMessage)