gpt4 book ai didi

java - 自定义 Jackson HttpMessageConverter 在 Spring 4.2 中不再有效

转载 作者:搜寻专家 更新时间:2023-10-30 20:01:02 25 4
gpt4 key购买 nike

我正在将一个应用程序从 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

@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 平台更新依赖版本之外,此代码的工作版本和非工作版本之间没有进行任何更改。我在文档中遗漏的 JSON 序列化是否有任何变化?

编辑

进一步的测试产生了奇怪的结果。我想测试检查以下内容:

  1. 我的自定义 HttpMessageConverter 是否真的被注册了?
  2. 另一个转换器是否正在覆盖/取代它?
  3. 这只是我的测试设置有问题吗?

所以,我添加了一个额外的测试并查看了输出:

@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 测试请求的方式发生了变化。

最佳答案

你的配置没问题。您的自定义转换器未调用 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#write

public final void write(final T t, final Type type, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
...
writeInternal(t, type, outputMessage);
...
}

AbstractGenericHttpMessageConverter#write(...) 中调用的方法 writeInternal(...) 具有三个参数 - (T t, Type type, HttpOutputMessage 输出消息)。您正在覆盖只有 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)

关于java - 自定义 Jackson HttpMessageConverter 在 Spring 4.2 中不再有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34951500/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com