2017-02-14 55 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使用和對於其他請求使用默認對象映射器。