2017-02-14 53 views
3

我有一个控制器,其响应是camelCase json值。现在我们用新版本重新编写代码,并且所需的响应位于snake_case中。如何根据条件应用弹簧消息转换器?

我添加了一个消息转换器和修改的对象映射器设置setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

public class ResponseJSONConverter extends MappingJackson2HttpMessageConverter { 

@Autowired 
public ResponseJSONConverter(ObjectMapper objectMapper) { 
    setObjectMapper(objectMapper); 
    } 
} 

我已经注册该转换器的弹簧和其工作按预期方式。现在我想让我的旧端点以camelCase的形式返回,以便为我的客户和使用snake_case的新端点提供向后兼容性。

我试图有一个更多的消息转换器与简单的对象映射器没有设置camelCase到Snake案例属性和春季注册。根据弹簧配置中声明的顺序,只有一个消息转换器被应用。

有什么办法可以做到这一点?根据条件加载消息转换器?

编辑

加了我的Spring配置文件

<beans xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:mvc="http://www.springframework.org/schema/mvc" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 
     xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context.xsd 
     http://www.springframework.org/schema/mvc 
     http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 

<bean id="moneySerializer" class="api.serialize.MoneySerializer"/> 
    <bean id="moneyDeserializer" class="api.serialize.MoneyDeserializer"/> 
    <bean id="serializationModule" class="api.serialize.SerializationModule"> 
     <constructor-arg index="0" ref="moneySerializer"/> 
     <constructor-arg index="1" ref="moneyDeserializer"/> 
    </bean> 

    <bean id="customObjectMapper" class="api.serialize.CustomObjectMapper" primary="true"> 
     <constructor-arg ref="serializationModule"/> 
    </bean> 
    <mvc:annotation-driven> 
     <mvc:message-converters register-defaults="true"> 
      <bean class="api.serialize.ResponseJSONConverterCamelCaseToSnakeCase" > 
       <constructor-arg ref="customObjectMapper"/> 
      </bean> 
      <bean class="api.serialize.ResponseJSONConverter"> 
       <constructor-arg ref="objectMapper"/> 
      </bean> 
     </mvc:message-converters> 

    </mvc:annotation-driven> 

    <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/> 

</beans> 

EDIT 2.0

我servlet.xml中

<mvc:annotation-driven> 
    <mvc:message-converters register-defaults="true"> 
     <bean class="com.tgt.promotions.api.serialize.ServiceJSONConverter"/> 
    </mvc:message-converters> 
</mvc:annotation-driven> 

CustomMessageConverter

public class ServiceJSONConverter extends MappingJackson2HttpMessageConverter { 

    @Autowired 
    public ServiceJSONConverter(SnakeCaseObjectMapper snakeCaseObjectMapper) { 
     setObjectMapper(snakeCaseObjectMapper); 
    } 
} 

自定义对象映射

@Component 
public class SnakeCaseObjectMapper extends ObjectMapper { 
    @Autowired 
    public SnakeCaseObjectMapper(PropertyNamingStrategy propertyNamingStrategy) { 
     setSerializationInclusion(JsonInclude.Include.NON_NULL); 
     setPropertyNamingStrategy(propertyNamingStrategy); 
    } 
} 

自定义属性命名反而有2个不同的对象映射器的战略

@Component 
public class CustomPropertyNamingStrategy extends PropertyNamingStrategy { 

    @Autowired 
    private HttpServletRequest request; 

    private final PropertyNamingStrategy legacyStrategy = PropertyNamingStrategy.LOWER_CASE; 
    private final PropertyNamingStrategy defaultStrategy = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; 


    @Override 
    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) { 
     return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName); 
    } 

    @Override 
    public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) { 
     return getStrategy().nameForField(config, field, defaultName); 
    } 

    @Override 
    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) { 
     return getStrategy().nameForGetterMethod(config, method, defaultName); 
    } 

    @Override 
    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) { 
     return getStrategy().nameForSetterMethod(config, method, defaultName); 
    } 

    private PropertyNamingStrategy getStrategy() { 
     if (isLegacyEndpoint(request)) { 
      return legacyStrategy; 
     } else { 
      return defaultStrategy; 
     } 
    } 

    private boolean isLegacyEndpoint(HttpServletRequest request) { 
     return request != null && request.getRequestURL() != null && !request.getRequestURL().toString().contains("/v3"); 
    } 
} 
+0

请问您可以添加您的spring xml配置或更多支持代码。 –

回答

0

,我建议建立一个铜自定实施PropertyNamingStrategy,因此使用其他2种策略:

public class AwesomePropertyNamingStrategy extends PropertyNamingStrategy { 

    private PropertyNamingStrategy legacyStrategy = PropertyNamingStrategy.LOWER_CASE; 
    private PropertyNamingStrategy defaultStrategy = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; 

    @Override 
    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) { 
    return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName); 
    } 

    // TODO: implement other nameForXXX methods 

    private PropertyNamingStrategy getStrategy() { 
    if (isLegacyEndpoint()) { 
     return legacyStrategy; 
    } else { 
     return defaultStrategy; 
    } 
    } 

    private boolean isLegacyEndpoint() { 
    // TODO: get hold of the RequestContext or some other thead-local context 
    // that allows you to know it's an old or a new endpoint 
    return false; 
    } 
} 

你应该想出一个办法传统和新模式之间切换:

  1. 使用端点URL通过访问请求上下文某种方式
  2. 如果您的旧端点使用不同的响应对象,请使用正在转换的对象的类来确定所有旧类的传统/常规或自定义@LegacyResponse批注。
+0

感谢您的回复。我试图做你所建议的任何事情。我建立了一个消息转换器,并按照您的建议传递customObjectMapper并设置AwesomePropertyNamingStrategy。但我得到的回应是小案例和蛇案策略的混合体。我需要做任何事情来解决这个问题吗? – Pramod

+0

您可以分享您提出的解决方案,特别是您区分旧版和新版的部分吗? –

+0

我在类中自动装配HttpServletRequest请求,并通过spring加载AwesomePropertyNamingStrategy作为组件。 – Pramod

0

那么,许多尝试后没有任何工作。最后最终定义了2个不同的servlet。一个不带任何版本,一个带有v1版本。

web。XML

 <servlet> 
      <servlet-name>snake-case</servlet-name> 
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
      <load-on-startup>1</load-on-startup> 
     </servlet> 

     <servlet-mapping> 
      <servlet-name>snake-case</servlet-name> 
      <url-pattern>/v1</url-pattern> 
     </servlet-mapping> 

     <servlet> 
      <servlet-name>camel-case</servlet-name> 
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
      <load-on-startup>1</load-on-startup> 
     </servlet> 

     <servlet-mapping> 
      <servlet-name>camel-case</servlet-name> 
      <url-pattern>/</url-pattern> 
     </servlet-mapping> 

因此限定两个servlet蛇区分servlet.xml中和骆驼的病例servlet.xml中。

蛇案例servlet.xml中

<mvc:annotation-driven> 
     <mvc:message-converters register-defaults="true"> 
      <bean class="com.tgt.promotions.api.serialize.DataJSONConverter"> 
      <constructor-arg ref="snakeCaseObjectMapper"/> 
      </bean> 
     </mvc:message-converters> 
    </mvc:annotation-driven> 

驼峰-servlet.xml中

<mvc:annotation-driven> 
     <mvc:message-converters register-defaults="true"> 
      <bean class="com.tgt.promotions.api.serialize.DataJSONConverter"> 
      <constructor-arg ref="objectMapper"/> 
      </bean> 
     </mvc:message-converters> 
    </mvc:annotation-driven> 

现在,对于/ V1 *任何请求,snakeCaseObjectMapper使用和对于其他请求使用默认对象映射器。