加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 自定义的Jackson HttpMessageConverter不再适用于Spring

发布时间:2020-12-14 16:34:44 所属栏目:Java 来源:网络整理
导读:我正在从 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版本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平台更新依赖关系,这个代码的工作版本和非工作版本之间都没有改变. 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());
        }
    }
}

…它工作正常.我的信封对象及其内容被序列化并被正确过滤.因此,在到达消息转换器之前,请求处理有问题,或者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#写

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

从AbstractGenericHttpMessageConverter#write(…)中调用的writeInternal(…)方法有三个参数 – (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,outputMessage);
    ...
}

所以,为了解决你的问题,而不是覆盖writeInternal(Object对象,HttpOutputMessage outputMessage),重写writeInternal(Object对象,Type类型,HttpOutputMessage outputMessage)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读