Printemps MVC @PathVariable obtenir tronqué

j'ai un contrôleur qui fournit un accès tranquille à l'information:

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
public ModelAndView getBlah(@PathVariable String blahName, HttpServletRequest request,
                            HttpServletResponse response) {

le problème que je rencontre est que si je frappe le serveur avec une variable path avec des caractères spéciaux, il est tronqué. Exemple: http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47

le paramètre blahName sera blah2010.08

cependant, l'appel à la demande.getRequestURI() contient toutes les informations transmises.

comment empêcher le ressort de tronquer le @PathVariable?

127
demandé sur phogel 2010-08-20 01:44:56

15 réponses

essayez une expression régulière pour l'argument @RequestMapping :

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
139
répondu earldouglas 2010-08-19 22:03:41

il s'agit probablement d'une relation étroite avec SPR-6164 . En bref, le framework essaie d'appliquer quelques astuces à L'interprétation de L'URI, en supprimant ce qu'il pense être des extensions de fichier. Cela aurait pour effet de transformer blah2010.08.19-02:25:47 en blah2010.08 , puisqu'il pense que .19-02:25:47 est une extension de fichier.

Comme décrit dans la question liée, vous pouvez désactiver ce comportement en déclarant votre propre DefaultAnnotationHandlerMapping bean dans le contexte de l'application, et établissant sa propriété useDefaultSuffixPattern à false . Cela annulera le comportement par défaut, et l'empêchera de molester vos données.

54
répondu skaffman 2010-08-19 23:04:44

Spring considère que tout ce qui se trouve derrière le dernier point est une extension de fichier comme .json ou .xml et le tronque pour récupérer votre paramètre.

donc si vous avez /{blahName} :

  • /param , /param.json , /param.xml ou /param.anything résultera en un paramètre avec la valeur param
  • /param.value.json , /param.value.xml ou /param.value.anything résultera en un param avec valeur param.value

si vous changez votre mapping en /{blahName:.+} comme suggéré, tout point, y compris le dernier, sera considéré comme faisant partie de votre paramètre:

  • /param résultera en un paramètre avec la valeur param
  • /param.json résultera en un paramètre avec la valeur param.json
  • /param.xml résultera en un paramètre avec la valeur param.xml
  • /param.anything résultera en un paramètre avec la valeur param.anything
  • /param.value.json résultera en un paramètre avec la valeur param.value.json
  • ...

si vous ne vous souciez pas de la reconnaissance d'extension, vous pouvez la désactiver en remplaçant mvc:annotation-driven automagic:

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="useSuffixPatternMatch" value="false"/>
</bean>

donc, encore une fois, si vous avez /{blahName} :

  • /param , /param.json , /param.xml ou /param.anything résultera en un paramètre avec la valeur param
  • /param.value.json , /param.value.xml ou /param.value.anything résultera en un paramètre avec la valeur param.value

Note: la différence par rapport à la configuration par défaut n'est visible que si vous avez une correspondance comme /something.{blahName} . Voir numéro du projet Resthub .

si vous souhaitez garder la gestion de l'extension, depuis le printemps 3.2 vous pouvez également définir la propriété useRegisteredSuffixPatternMatch de RequestMappingHandlerMapping bean afin de maintenir la reconnaissance de suffixPattern activée mais limitée à l'extension enregistrée.

ici vous définissez seulement les extensions JSON et xml:

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="useRegisteredSuffixPatternMatch" value="true"/>
</bean>

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false"/>
    <property name="favorParameter" value="true"/>
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>

notez que mvc: annotation-driven accepte maintenant une option contentNegotiation pour fournir un bean personnalisé mais la propriété de RequestMappingHandlerMapping doit être changée en true (false par défaut)) (cf. https://jira.springsource.org/browse/SPR-7632 ).

pour cette raison, vous devez toujours annuler toute la configuration mvc:annotation-driven. J'ai ouvert un billet pour Spring pour demander une demande personnaliséemappinghandlermapping: https://jira.springsource.org/browse/SPR-11253 . Merci de voter si vous êtes intéressé dans.

tout en dépassant, attention à prendre également en compte la gestion de l'exécution sur mesure primordial. Sinon, tous vos mappages d'Exception personnalisés échoueront. Vous devrez réutiliser les décodeurs de messagerie avec un haricot de la liste:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />

<util:list id="messageConverters">
    <bean class="your.custom.message.converter.IfAny"></bean>
    <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</util:list>

<bean name="exceptionHandlerExceptionResolver"
      class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
    <property name="order" value="0"/>
    <property name="messageConverters" ref="messageConverters"/>
</bean>

<bean name="handlerAdapter"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
            <property name="conversionService" ref="conversionService" />
            <property name="validator" ref="validator" />
        </bean>
    </property>
    <property name="messageConverters" ref="messageConverters"/>
</bean>

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>

j'ai mis en œuvre, dans le projet open source Resthub dont je fais partie, un ensemble d'essais sur ces sujets: Voir https://github.com/resthub/resthub-spring-stack/pull/219/files et https://github.com/resthub/resthub-spring-stack/issues/217

28
répondu bmeurant 2017-04-28 09:47:32

Tout ce qui suit le dernier point est interprété comme une extension de fichier et est coupé par défaut.

Dans votre config xml du printemps, vous pouvez ajouter DefaultAnnotationHandlerMapping et définir useDefaultSuffixPattern à false (par défaut true ).

ouvrez donc votre ressort xml mvc-config.xml (ou comment il s'appelle) et ajoutez

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="useDefaultSuffixPattern" value="false" />
</bean>

maintenant votre @PathVariable blahName (et tous les autres, aussi) devrait contenir le nom complet incluant tous les points.

EDIT: voici un lien vers l'api du ressort

15
répondu Jan 2010-10-25 08:40:21

j'ai aussi rencontré le même problème, et mettre la propriété à false ne m'a pas aidé non plus. Cependant, L'API dit :

Note que les chemins qui comprennent un ".xxx "suffixe ou fin avec" / " déjà ne sera pas transformé en utilisant le modèle de suffixe par défaut dans tous les cas.

j'ai essayé d'ajouter" /end " à mon URL RESTful, et le problème a disparu. Je ne suis pas satisfait de la solution, mais ça a marché.

BTW, Je ne sais pas ce que les concepteurs du ressort pensaient quand ils ont ajouté cette "fonctionnalité" et l'ont ensuite activée par défaut. À mon humble avis, il doit être retiré.

7
répondu Steve11235 2012-10-30 16:52:22

utilisant la classe de configuration Java correcte:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
    {
        configurer.favorPathExtension(false);
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer)
    {
        configurer.setUseSuffixPatternMatch(false);
    }
}
5
répondu jebeaudet 2015-03-05 16:59:09

j'ai résolu par ce hack

1) Ajouté HttpServletRequest in @PathVariable comme ci-dessous

 @PathVariable("requestParam") String requestParam, HttpServletRequest request) throws Exception { 

2) Obtenir l'URL directement (à ce niveau Pas de troncature) dans la requête

request.getPathInfo() 

Spring MVC @PathVariable with dot (.) devient tronqué

4
répondu Kanagavelu Sugumar 2017-05-23 10:31:23

je viens de tomber sur ceci et les solutions ici n'ont généralement pas fonctionné comme je l'espérais.

je suggère d'utiliser une expression de SpEL et des mappages multiples, par exemple

@RequestMapping(method = RequestMethod.GET, 
    value = {Routes.BLAH_GET + "/{blahName:.+}", 
             Routes.BLAH_GET + "/{blahName}/"})
3
répondu Mark Elliot 2011-07-27 16:44:26

le problème d'extension de fichier n'existe que si le paramètre se trouve dans la dernière partie de l'URL. Changement

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")

à

@RequestMapping(
   method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/safe")

et tout ira bien à nouveau-

3
répondu chrismarx 2013-07-12 15:52:27

Si vous pouvez modifier l'adresse que les demandes sont envoyées à, la solution simple serait d'ajouter une barre oblique (et aussi dans le @RequestMapping ):

/path/{variable}/

donc la carte ressemblerait à:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/")

Voir aussi Spring MVC @PathVariable avec le point (.) devient tronqué .

3
répondu Michał Rybak 2017-05-23 12:34:16
//in your xml dispatcher  add this property to your default annotation mapper bean as follow
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="alwaysUseFullPath" value="true"></property>
</bean>       
3
répondu Fareed Alnamrouti 2014-03-25 17:26:40

ajouter ":.+ "a fonctionné pour moi, mais pas avant que j'ai enlevé les crochets bouclés extérieurs.

value = {"/username/{id:.+}"} n'a pas fonctionné

value = "/username/{id:.+}" œuvres

Hope I help someone :]

3
répondu Martin Čejka 2016-01-15 12:18:40

Java configuration basée sur une solution pour éviter la troncature (à l'aide d'un non-déconseillé de classe):

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
public class PolRepWebConfig extends WebMvcConfigurationSupport {

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        final RequestMappingHandlerMapping handlerMapping = super
                .requestMappingHandlerMapping();
        // disable the truncation after .
        handlerMapping.setUseSuffixPatternMatch(false);
        // disable the truncation after ;
        handlerMapping.setRemoveSemicolonContent(false);
        return handlerMapping;
    }
}

Source: http://www.javacodegeeks.com/2013/01/spring-mvc-customizing-requestmappinghandlermapping.html

mise à jour:

j'ai réalisé avoir quelques problèmes avec L'auto-configuration de démarrage à ressort quand j'ai utilisé l'approche ci-dessus (certains auto-configuration ne reçoit pas efficace.)

à la place, j'ai commencé à utiliser l'approche BeanPostProcessor . Il semblait être à travailler mieux.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    private static final Logger logger = LoggerFactory
            .getLogger(MyBeanPostProcessor.class);

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof RequestMappingHandlerMapping) {
            setRemoveSemicolonContent((RequestMappingHandlerMapping) bean,
                    beanName);
            setUseSuffixPatternMatch((RequestMappingHandlerMapping) bean,
                    beanName);
        }
        return bean;
    }

    private void setRemoveSemicolonContent(
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            String beanName) {
        logger.info(
                "Setting 'RemoveSemicolonContent' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
                beanName);
        requestMappingHandlerMapping.setRemoveSemicolonContent(false);
    }

    private void setUseSuffixPatternMatch(
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            String beanName) {
        logger.info(
                "Setting 'UseSuffixPatternMatch' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
                beanName);
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
    }
}

inspiré de: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html

2
répondu burcakulug 2015-03-23 17:48:26

si vous êtes sûr que votre texte ne correspond à aucune extension par défaut, vous pouvez utiliser le code ci-dessous:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}
2
répondu Bassem Reda Zohdy 2015-09-29 18:23:54

Ma meilleure solution pour empêcher le ressort MVC @PathVariable d'être tronqué est d'ajouter slash arrière à la fin de la variable path.

par exemple:

@RequestMapping(value ="/email/{email}/")

ainsi, la demande ressemblera à:

http://localhost:8080/api/email/test@test.com/
1
répondu Stas 2017-02-07 10:02:05