2017-02-14 1 views
3

J'ai un contrôleur dont la réponse est la valeur camelCase json. Maintenant, nous réécrivons le code avec la nouvelle version et la réponse requise est dans snake_case.comment appliquer les convertisseurs de message de printemps basés sur la condition?

J'ai ajouté un convertisseur de message et objet modifié mappeur pour définir setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

public class ResponseJSONConverter extends MappingJackson2HttpMessageConverter { 

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

Je me suis inscrit ce convertisseur avec ressort et son travail comme prévu. Maintenant, je veux que mes anciens points de terminaison reviennent dans camelCase pour une compatibilité ascendante pour mes clients et de nouveaux points de terminaison avec snake_case.

J'ai essayé d'avoir un autre convertisseur de message avec un simple mappeur d'objet sans régler la propriété de cas de camelCase à Snake et enregistré avec le ressort. Un seul convertisseur de message est appliqué en fonction de l'ordre déclaré dans la configuration du ressort.

Y a-t-il un moyen d'y parvenir? Chargement du convertisseur de message en fonction de la condition?

EDIT

Ajouté mon fichier de configuration de printemps

<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

mon 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); 
    } 
} 

objet personnalisé Mapper

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

Stratégie de dénomination propriété personnalisée

@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

pouvez-vous s'il vous plaît ajouter votre configuration printemps xml ou plus de code de soutien. –

Répondre

0

Au lieu d'avoir 2 différents objets-cartographes, je suggère la création d'un cu la mise en œuvre de STOM PropertyNamingStrategy, en utilisant les 2 autres stratégies en conséquence:

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; 
    } 
} 

Vous devriez trouver une façon de basculer entre l'héritage et le nouveau mode:

  1. Utilisation de l'URL du noeud final en accédant au contexte de demande d'une certaine manière
  2. Si votre ancien terminal utilise des objets de réponse différents, utilisez la classe de l'objet en cours de conversion pour déterminer l'héritage/normal ou votre propre annotation personnalisée @LegacyResponse sur toutes les anciennes classes à la place.
+0

Merci pour la réponse. J'ai essayé de faire ce que tu as suggéré. J'ai créé un convertisseur de message et en passant customObjectMapper et en définissant AwesomePropertyNamingStrategy comme vous l'avez suggéré. Mais la réponse que je reçois est un mélange de stratégie à la fois petite et snake_case. Ai-je besoin de faire quelque chose pour résoudre ce problème? – Pramod

+0

Pouvez-vous partager la solution que vous avez trouvée, en particulier la partie où vous faites la différence entre l'ancien et le nouveau? –

+0

Je suis autowiring demande HttpServletRequest dans la classe et en chargeant AwesomePropertyNamingStrategy en tant que composant par le printemps. – Pramod

0

Eh bien, rien n'a fonctionné après de nombreuses tentatives. Finalement fini par définir 2 servlets différents. un sans aucune version et un avec la version 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> 

conséquent défini deux servlets serpent-case-servlet.xml et camel-case-servlet.xml.

serpent cas 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> 

chameau cas 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> 

Maintenant, pour toute demande avec/v1 *, snakeCaseObjectMapper est utilisé et pour les autres demandes, le mappeur d'objet par défaut est utilisé.