Spring Boot:请求和响应的不同 ObjectMapper 实例

Posted

技术标签:

【中文标题】Spring Boot:请求和响应的不同 ObjectMapper 实例【英文标题】:Spring Boot : Different ObjectMapper instances for Request and Response 【发布时间】:2017-07-30 04:55:07 【问题描述】:

我的 Spring Boot 应用程序中有以下控制器:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<ResponseDto<MyClass> process(@RequestBody RequestDto<MyClass> request)
    return null;

MyClass 有一个字段,比方说“myField”,我想要不同的NamingStrategyconfiguration 用于该字段的请求和响应(这是因为我不想只为一个字段创建一个新类)。我已将ObjectMapper 实例配置如下:

@Bean
public ObjectMapper objectMapper()
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setPropertyNamingStrategy(namingStrategy);
    return objectMapper;

这将用于请求和响应(即反序列化和序列化),在 spring boot 中有什么方法可以指示控制器使用不同的ObjectMapper 实例?

【问题讨论】:

AFAIK 没有直接的方法。考虑实现你自己的AbstractJackson2HttpMessageConverter 【参考方案1】:

您可以通过 content negotiation 解决它。首先,定义您的自定义 HttpMessageConverter。在以下示例中,我定义了一个自定义转换器,当请求 Content-Type 标头设置为 application/test+json 时应用该转换器:

@Bean
public HttpMessageConverters customConverters() 
    final AbstractJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(new ObjectMapper());
    converter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("application/test+json")));

    return new HttpMessageConverters(true, Collections.singletonList(converter));

为简单起见,我使用了新创建的 ObjectMapper - 在您的情况下,您必须在此处传递以前配置的对象。

接下来是告诉您的操作只接受 appliction/test+json 请求(请记住,从现在开始,它需要 Content-Type:application/test+json 标头出现在对该端点的每个请求中):

@RequestMapping(method = RequestMethod.POST, consumes = "application/test+json")
public ResponseEntity<ResponseDto<MyClass> process(@RequestBody RequestDto<MyClass> request)
    return null;

最后一件事是确保当您调用此端点时,Content-Type:application/test+json 标头已设置。当然,您可以为所需的内容类型使用任何其他名称,提供的名称只是一个示例。

【讨论】:

【参考方案2】:

您可以在 ObjectMapper 中使用反序列化修饰符通过模块在对象反序列化时覆盖启用的功能集。这个应该可以解决问题:

    public class FeatureModifyingBeanDeserializerModifier extends BeanDeserializerModifier 

        private Collection<Class<?>> modifiedClasses;

        public FeatureModifyingBeanDeserializerModifier(Collection<Class<?>> modifiedClasses) 
            this.modifiedClasses = Collections.unmodifiableSet(new HashSet<Class<?>>(modifiedClasses));
        

        @Override
        public JsonDeserializer<?> modifyDeserializer(
                DeserializationConfig config, BeanDescription beanDesc, final JsonDeserializer<?> deserializer) 
            JsonDeserializer<?> result = deserializer;
            Class<?> beanClass = beanDesc.getBeanClass();

            if (modifiedClasses.contains(beanClass)) 
                result = new FeatureModifyingStdDeserializer(deserializer, beanClass);
            
            return result;
        

        private static class FeatureModifyingStdDeserializer extends StdDeserializer<Object> 

            private JsonDeserializer<?> deserializer;

            private FeatureModifyingStdDeserializer(
JsonDeserializer<?> deserializer, Class<?> beanClass) 
                super(beanClass);
                this.deserializer = deserializer;
            

            @Override
            public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException 
                p.enable(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
                return deserializer.deserialize(p, ctxt);
            
              
    

您必须将其注册为 ObjectMapper 的模块,如下所示:

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();

module.setDeserializerModifier(new FeatureModifyingBeanDeserializerModifer(Arrays.asList(Journey.class)));
objectMapper.registerModule(module);

对于序列化,您可以向 Journey 类添加一个 @JsonSerialize 注释,并以您想要的任何方式对其进行序列化。如果您需要编写未转义的字符串,您可以使用 JsonGenerator 中的 writeRaw。

【讨论】:

【参考方案3】:

一个肮脏的技巧:您可以为 MyClass 编写自定义序列化器和反序列化器,在那里您明确使用两个单独的对象映射器,一个用于序列化(用于响应),第二个用于反序列化(用于请求)。

但最好是想办法显式自定义spring object mapper。

【讨论】:

以上是关于Spring Boot:请求和响应的不同 ObjectMapper 实例的主要内容,如果未能解决你的问题,请参考以下文章

nginx代理转发url接口请求路径到spring boot后端实现真正响应

Spring Boot - 缓慢的请求/响应

向服务器发送 POST 请求,服务器的响应为空,Angular - Spring-boot

Spring Boot 2.x基础教程:如何扩展XML格式的请求和响应

我们如何在使用logback.xml和MDC的spring-boot日志中获取用户请求,响应和request-id以及process-id?

Spring Boot + Spring Security 应用程序中 POST/PUT/DELETE 请求的 403 响应