web-dev-qa-db-fra.com

Spring MVC @PathVariable se tronque

J'ai un contrôleur qui fournit un accès RESTful à 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 de chemin d'accès avec des caractères spéciaux, elle est tronquée. Par exemple: http: // localhost: 8080/blah-server/blah/get/blah2010.08.19-02: 25: 47

Le paramètre blahName sera blah2010.08

Cependant, l'appel à request.getRequestURI () contient toutes les informations transmises.

Avez-vous une idée de la façon d'empêcher Spring de tronquer la @PathVariable?

132
phogel

Essayez une expression régulière pour le @RequestMapping argument:

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

Ceci est probablement étroitement lié à SPR-6164 . En bref, le framework essaie d’appliquer quelques intelligences à l’interprétation d’URI, en supprimant ce qu’il considère être des extensions de fichiers. Cela aurait pour effet de tourner blah2010.08.19-02:25:47 en blah2010.08, puisqu'il pense que le .19-02:25:47 est une extension de fichier.

Comme décrit dans le problème lié, vous pouvez désactiver ce comportement en déclarant votre propre bean DefaultAnnotationHandlerMapping dans le contexte de l'application et en définissant sa propriété useDefaultSuffixPattern sur false. Cela remplacera le comportement par défaut et l'empêchera de molester vos données.

55
skaffman

Spring considère que tout ce qui se cache derrière le dernier point est une extension de fichier telle que .jsonou .xml et le tronquer pour récupérer votre paramètre.

Donc, si vous avez /{blahName}:

  • /param, /param.json, /param.xml ou /param.anything donnera un paramètre de valeur param
  • /param.value.json, /param.value.xml ou /param.value.anything donnera un paramètre de valeur param.value

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

  • /param donnera un paramètre de valeur param
  • /param.json donnera un paramètre de valeur param.json
  • /param.xml donnera un paramètre de valeur param.xml
  • /param.anything donnera un paramètre de valeur param.anything
  • /param.value.json donnera un paramètre de 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 donnera un paramètre de valeur param
  • /param.value.json, /param.value.xml ou /param.value.anything donnera un paramètre de valeur param.value

Remarque: la différence par rapport à la configuration par défaut n’est visible que si vous avez un mapping tel que /something.{blahName}. Voir Numéro du projet Resthub .

Si vous souhaitez conserver la gestion des extensions, vous pouvez également, depuis Spring 3.2, définir la propriété useRegisteredSuffixPatternMatch du bean RequestMappingHandlerMapping afin de maintenir la reconnaissance suffixPattern activée, mais limitée à l'extension enregistrée.

Ici, vous définissez uniquement 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 désormais une option contentNegotiation pour fournir un bean personnalisé, mais que la propriété de RequestMappingHandlerMapping doit être remplacée par true (par défaut, false) (cf. https://jira.springsource.org//SPR-7632 ).

Pour cette raison, vous devez toujours remplacer toute la configuration mvc: annotation. J'ai ouvert un ticket à Spring pour demander un RequestMappingHandlerMapping personnalisé: https://jira.springsource.org/browse/SPR-1125 . S'il vous plaît voter si vous êtes intéressé par.

Lors du remplacement, veillez à prendre également en compte le remplacement personnalisé de la gestion d'exécution. Sinon, tous vos mappages d'exceptions personnalisés échoueront. Vous devrez réutiliser messageCoverters avec un bean list:

<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 implémenté, dans le projet open source Resthub dont je fais partie, une série de tests sur ces sujets: voir https://github.com/resthub/resthub-spring-stack/pull/219/files et https://github.com/resthub/resthub-spring-stack/issues/217

29
bmeurant

Tout ce qui suit le dernier point est interprété comme une extension de fichier et coupé par défaut.
Dans votre print XML config, vous pouvez ajouter DefaultAnnotationHandlerMapping et définir useDefaultSuffixPattern sur false (la valeur par défaut est true).

Alors ouvrez votre printemps xml mvc-config.xml (ou quelle que soit sa dénomination) et ajoute

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

Maintenant votre @PathVariableblahName (et tous les autres) doit contenir le nom complet, y compris tous les points.

EDIT: Voici un lien vers l'api de printemps

15
Jan

J'ai également rencontré le même problème, et définir la propriété sur false ne m'a pas aidé non plus. Cependant, l'API dit :

Notez que les chemins comportant un suffixe ".xxx" ou se terminant par "/" ne seront déjà pas transformés 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 content de la solution, mais cela a fonctionné.

BTW, je ne sais pas ce que les designers de Spring pensaient en ajoutant cette "fonctionnalité" puis en l'activant par défaut. IMHO, il devrait être supprimé.

7
Steve11235

Utilisation de la classe de configuration correcte Java:

@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);
    }
}
7
jebeaudet

J'ai résolu par ce bidouillage

1) Ajout de HttpServletRequest dans @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 avec le point (.) Est tronqué

4
Kanagavelu Sugumar
//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
Fareed Alnamrouti

Je me suis heurté à cela et les solutions proposées ne fonctionnaient généralement pas comme prévu.

Je suggère d'utiliser une expression SpEL et plusieurs mappages, par exemple.

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

l'ajout du ":. +" a fonctionné pour moi, mais pas avant d'avoir supprimé les accolades extérieures.

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

value = "/username/{id:.+}" travaux

J'espère avoir aidé quelqu'un:]

3
Martin Čejka

Si vous pouvez modifier l’adresse à laquelle les demandes sont envoyées, une solution simple consisterait à leur ajouter une barre oblique de fin (et également dans le champ @RequestMapping valeur):

/path/{variable}/

donc la cartographie ressemblerait à:

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

Voir aussi Spring MVC @PathVariable avec un point (.) Est tronqué .

3
Michał Rybak

Le problème d'extension de fichier n'existe que si le paramètre est 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
chrismarx

Solution de configuration basée sur Java pour empêcher la troncature (en utilisant une classe non dépréciée):

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

UPDATE:

J'ai réalisé que la configuration automatique de Spring Boot posait quelques problèmes lorsque j'ai utilisé l'approche ci-dessus (certaines configurations automatiques ne sont pas efficaces).

Au lieu de cela, j'ai commencé à utiliser l'approche BeanPostProcessor. Cela semblait mieux fonctionner.

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
burcakulug

si vous êtes sûr que votre texte ne correspond à aucune des extensions 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
Bassem Reda Zohdy

Ma solution préférable pour éviter que Spring MVC @PathVariable soit tronqué consiste à ajouter une barre oblique à la fin de la variable de chemin.

Par exemple:

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

Ainsi, la demande ressemblera à:

http://localhost:8080/api/email/[email protected]/
1
Johnny

Le problème que vous rencontrez est dû à ressort qui interprète la partie last de l'uri après le point (.) en tant que extension du fichier comme .json ou .xml. Ainsi, lorsque spring essaie de résoudre la variable path, il tronque simplement le reste des données après avoir rencontré un point (.) À la fin de l'URI. Remarque: Ceci ne se produit également que si vous conservez la variable de chemin d'accès à la fin de l'URI.

Par exemple, considérez uri: https: //localhost/example/gallery.df/link.ar

@RestController
public class CustomController {
    @GetMapping("/example/{firstValue}/{secondValue}")
    public void example(@PathVariable("firstValue") String firstValue,
      @PathVariable("secondValue") String secondValue) {
        // ...  
    }
}

Dans l'URL ci-dessus, firstValue = "gallery.df" et secondValue = "link", le dernier bit après le. est tronqué lorsque la variable de chemin est interprétée.

Donc, pour éviter cela, il y a deux manières possibles:

1.) Utilisation d'un mappage d'expressions rationnelles

Utiliser une regex à la fin du mapping

@GetMapping("/example/{firstValue}/{secondValue:.+}")   
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

En utilisant +, nous indiquons que toute valeur après le point fera également partie de la variable de chemin.

2.) Ajouter une barre oblique à la fin de notre @PathVariable

@GetMapping("/example/{firstValue}/{secondValue}/")
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

Ceci inclura notre deuxième variable le protégeant du comportement par défaut de Spring.

3) En remplaçant la configuration webmvc par défaut de Spring

Spring fournit des moyens de remplacer les configurations par défaut importées à l'aide des annotations @ EnableWebMvc. Nous pouvons personnaliser la configuration de Spring MVC en déclarant notre propre DefaultAnnotationHandlerMapping son bean dans le contexte de l'application et en définissant son useDefaultSuffixPattern propriété à false. Exemple:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

        RequestMappingHandlerMapping handlerMapping
          = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        return handlerMapping;
    }
}

Gardez à l'esprit que le remplacement de cette configuration par défaut affecte toutes les URL.

Remarque: Nous étendons ici la classe WebMvcConfigurationSupport pour remplacer les méthodes par défaut. Il existe un autre moyen de remplacer les configurations de deault en implémentant l'interface WebMvcConfigurer. Pour plus de détails à ce sujet, lisez: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

0