Spring MVC @PathVariable wird abgeschnitten

141

Ich habe einen Controller, der RESTful Zugriff auf Informationen bietet:

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

Das Problem, das ich habe, ist, dass wenn ich den Server mit einer Pfadvariablen mit Sonderzeichen treffe, dieser abgeschnitten wird. Zum Beispiel: http: // localhost: 8080 / blah-server / blah / get / blah2010.08.19-02: 25: 47

Der Parameter blahName lautet blah2010.08

Der Aufruf von request.getRequestURI () enthält jedoch alle übergebenen Informationen.

Haben Sie eine Idee, wie Sie verhindern können, dass Spring die @PathVariable abschneidet?

Phogel
quelle
Es scheint, dass dies in Spring 3.2-M2 behoben wurde: Siehe Zulassen gültiger Dateierweiterungspfade für die Angabe von Inhaltsaushandlungen und deren Dokumentation .
Arjan

Antworten:

149

Versuchen Sie einen regulären Ausdruck für das @RequestMappingArgument:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
Earldouglas
quelle
1
Vielen Dank für die Antwort. Dies hat mir geholfen, einen Fall zu lösen, in dem Benutzernamen irgendwie gekürzt wurden. (-: Die andere Option mit 'useDefaultSuffixPattern' war keine Option, da wir @Configuration-Frühlingsklassen anstelle von XML verwenden.
evandongen
3
Das funktioniert, aber welche Bedeutung hat der Doppelpunkt in der Regex?
Noah Yetter
6
Noah, ich habe das schon lange nicht mehr benutzt, aber ich denke, der Doppelpunkt trennt den regulären Ausdruck vom Argumentnamen, an den er gebunden werden soll.
Earldouglas
3
Wir hatten ein ähnliches Problem /item/[email protected], alles nachdem @ abgeschnitten wurde, wurde dies durch Hinzufügen eines weiteren Schrägstrichs gelöst /item/[email protected]/
Titi Wangsa bin Damhore
58

Dies hängt wahrscheinlich eng mit SPR-6164 zusammen . Kurz gesagt, das Framework versucht, einige Smarts auf die URI-Interpretation anzuwenden, indem es entfernt, was es für Dateierweiterungen hält. Dies hätte den Effekt des Drehens blah2010.08.19-02:25:47in blah2010.08, da es das denkt , .19-02:25:47ist eine Dateierweiterung.

Wie im verknüpften Problem beschrieben, können Sie dieses Verhalten deaktivieren, indem Sie Ihre eigene DefaultAnnotationHandlerMappingBean im App-Kontext deklarieren und ihre useDefaultSuffixPatternEigenschaft auf festlegen false. Dadurch wird das Standardverhalten außer Kraft gesetzt und Ihre Daten werden nicht mehr belästigt.

Skaffman
quelle
3
Das standardmäßige Aktivieren der auf Erweiterungen basierenden Inhaltsverhandlung scheint eine so seltsame Wahl zu sein. Wie viele Systeme stellen in der Praxis tatsächlich dieselbe Ressource in unterschiedlichen Formaten zur Verfügung?
Affe
Ich habe es am Morgen versucht und hatte immer noch abgeschnittene Pfadvariablen.
Phogel
30
+1 für eine gute Antwort und auch für die Verwendung des Ausdrucks "Belästigung Ihrer Daten"
Chris Thompson
11
Für Spring 3.1-Benutzer: Wenn Sie stattdessen die neue RequestMappingHandlerMappingverwenden, lautet die festzulegende Eigenschaft useSuffixPatternMatch(auch auf false). @Ted: Das verknüpfte Problem erwähnt, dass sie in 3.2 hoffen, ein bisschen mehr Kontrolle hinzuzufügen, damit es nicht alles oder nichts sein muss.
Nick
1
In Spring 4.2 ist dies etwas einfacher zu konfigurieren. Wir verwenden Java-Konfigurationsklassen und erweitern die, WebMvcConfigurationSupportdie einen einfachen Hook bietet: public void configurePathMatch(PathMatchConfigurer configurer)- Überschreiben Sie diese einfach und richten Sie den Pfad so ein, wie Sie möchten.
pmckeown
31

Spring ist der Ansicht, dass alles, was hinter dem letzten Punkt steht, eine Dateierweiterung wie .jsonoder ist, .xmlund schneidet sie ab, um Ihren Parameter abzurufen.

Also, wenn Sie haben /{blahName}:

  • /param, /param.json, /param.xmlOder /param.anythingwird in einem param mit Wert führenparam
  • /param.value.json, /param.value.xmloder /param.value.anythingführt zu einem Parameter mit Wertparam.value

Wenn Sie Ihre Zuordnung /{blahName:.+}wie vorgeschlagen ändern , wird jeder Punkt, einschließlich des letzten, als Teil Ihres Parameters betrachtet:

  • /param führt zu einem Parameter mit Wert param
  • /param.json führt zu einem Parameter mit Wert param.json
  • /param.xml führt zu einem Parameter mit Wert param.xml
  • /param.anything führt zu einem Parameter mit Wert param.anything
  • /param.value.json führt zu einem Parameter mit Wert param.value.json
  • ...

Wenn Sie die Erweiterungserkennung nicht interessieren, können Sie sie deaktivieren, indem Sie mvc:annotation-drivenautomagic überschreiben :

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

Also noch einmal, wenn Sie haben /{blahName}:

  • /param, /param.json, /param.xmlOder /param.anythingwird in einem param mit Wert führenparam
  • /param.value.json, /param.value.xmloder /param.value.anythingführt zu einem Parameter mit Wertparam.value

Hinweis: Der Unterschied zur Standardkonfiguration ist nur sichtbar, wenn Sie eine Zuordnung wie haben /something.{blahName}. Siehe Resthub-Projektproblem .

Wenn Sie die Erweiterungsverwaltung beibehalten möchten, können Sie seit Spring 3.2 auch die useRegisteredSuffixPatternMatch-Eigenschaft der RequestMappingHandlerMapping-Bean festlegen, um die SuffixPattern-Erkennung aktiviert, aber auf die registrierte Erweiterung beschränkt zu halten.

Hier definieren Sie nur JSON- und XML-Erweiterungen:

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

Beachten Sie, dass mvc: annotation-powered jetzt eine contentNegotiation-Option zum Bereitstellen einer benutzerdefinierten Bean akzeptiert, die Eigenschaft von RequestMappingHandlerMapping jedoch in true geändert werden muss (Standardwert false) (vgl. Https://jira.springsource.org/browse/SPR-7632) ).

Aus diesem Grund müssen Sie immer noch die gesamte mvc: annotation-gesteuerte Konfiguration überschreiben. Ich habe ein Ticket für Spring geöffnet, um nach einem benutzerdefinierten RequestMappingHandlerMapping zu fragen: https://jira.springsource.org/browse/SPR-11253 . Bitte stimmen Sie ab, wenn Sie interessiert sind.

Berücksichtigen Sie beim Überschreiben auch das Überschreiben der benutzerdefinierten Ausführungsverwaltung. Andernfalls schlagen alle Ihre benutzerdefinierten Ausnahmezuordnungen fehl. Sie müssen messageCoverters mit einer List Bean wiederverwenden:

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

Ich habe in dem Open-Source-Projekt Resthub , an dem ich beteiligt bin, eine Reihe von Tests zu diesen Themen implementiert: siehe https://github.com/resthub/resthub-spring-stack/pull/219/files und https: // github.com/resthub/resthub-spring-stack/issues/217

bmeurant
quelle
16

Alles nach dem letzten Punkt wird als Dateierweiterung interpretiert und standardmäßig abgeschnitten.
In Ihrer Feder Config xml können Sie hinzufügen DefaultAnnotationHandlerMappingund Satz useDefaultSuffixPatternauf false(Standard true).

Öffnen Sie also Ihre Spring-XML mvc-config.xml(oder wie auch immer sie heißt) und fügen Sie sie hinzu

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

Jetzt sollte Ihr @PathVariable blahName(und auch alle anderen) den vollständigen Namen einschließlich aller Punkte enthalten.

EDIT: Hier ist ein Link zur Spring API

Jan.
quelle
Ich habe es nicht versucht, aber andere behaupten, Sie müssten es <mvc:annotation-driven />dann gegebenenfalls auch entfernen .
Arjan
7

Ich bin auch auf das gleiche Problem gestoßen, und das Setzen der Eigenschaft auf false hat mir auch nicht geholfen. Die API sagt jedoch :

Beachten Sie, dass Pfade, die das Suffix ".xxx" enthalten oder bereits mit "/" enden, in keinem Fall mit dem Standardsuffixmuster transformiert werden.

Ich habe versucht, meiner RESTful-URL "/ end" hinzuzufügen, und das Problem ist behoben. Ich bin mit der Lösung nicht zufrieden, aber es hat funktioniert.

Übrigens weiß ich nicht, was die Spring-Designer gedacht haben, als sie diese "Funktion" hinzugefügt und dann standardmäßig aktiviert haben. IMHO sollte es entfernt werden.

Steve11235
quelle
Genau. Das hat mich vor kurzem gebissen.
Lambda
7

Verwenden der richtigen Java-Konfigurationsklasse:

@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);
    }
}
jebeaudet
quelle
Das hat bei mir super funktioniert. Laufen auf Tomcat Spring Version 4.3.14
Dave
4

Ich habe durch diesen Hack gelöst

1) HttpServletRequest in @PathVariable wie unten hinzugefügt

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

2) Holen Sie sich die URL direkt (auf dieser Ebene keine Kürzung) in die Anfrage

request.getPathInfo() 

Spring MVC @PathVariable mit Punkt (.) Wird abgeschnitten

Kanagavelu Sugumar
quelle
3

Ich bin gerade darauf gestoßen und die Lösungen hier funktionierten im Allgemeinen nicht wie erwartet.

Ich schlage vor, einen SpEL-Ausdruck und mehrere Zuordnungen zu verwenden, z

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

Das Dateierweiterungsproblem besteht nur, wenn sich der Parameter im letzten Teil der URL befindet. Veränderung

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

zu

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

und alles wird wieder gut

Chrismarx
quelle
3

Wenn Sie die Adresse bearbeiten können, an die Anforderungen gesendet werden, besteht eine einfache Lösung darin, ihnen einen abschließenden Schrägstrich (und auch den @RequestMappingWert) hinzuzufügen :

/path/{variable}/

Das Mapping würde also so aussehen:

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

Siehe auch Spring MVC @PathVariable mit Punkt (.) Wird abgeschnitten .

Michał Rybak
quelle
3
//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>       
Fareed Alnamrouti
quelle
3

Das Hinzufügen des ":. +" funktionierte für mich, aber erst, als ich die äußeren geschweiften Klammern entfernte.

value = {"/username/{id:.+}"} hat nicht funktioniert

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

Hoffe ich habe jemandem geholfen:]

Martin Čejka
quelle
2

Java-basierte Konfigurationslösung zum Verhindern des Abschneidens (unter Verwendung einer nicht veralteten Klasse):

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

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

AKTUALISIEREN:

Ich habe festgestellt, dass einige Probleme mit der automatischen Konfiguration von Spring Boot aufgetreten sind, als ich den oben beschriebenen Ansatz verwendet habe (einige automatische Konfigurationen werden nicht wirksam).

Stattdessen begann ich, den BeanPostProcessorAnsatz zu verwenden. Es schien besser zu funktionieren.

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

Inspiriert von: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html

Burcakulug
quelle
2

Wenn Sie sicher sind, dass Ihr Text keiner der Standarderweiterungen entspricht, können Sie den folgenden Code verwenden:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}
Bassem Reda Zohdy
quelle
1

Meine bevorzugte Lösung, um zu verhindern, dass Spring MVC @PathVariable abgeschnitten wird, besteht darin, am Ende der Pfadvariablen einen abschließenden Schrägstrich hinzuzufügen.

Beispielsweise:

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

Die Anfrage sieht also folgendermaßen aus:

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

Das Problem, mit dem Sie konfrontiert sind, ist darauf zurückzuführen, dass Spring den letzten Teil des Uri nach dem Punkt (.) Als Dateierweiterung wie .json oder .xml interpretiert. Wenn spring also versucht, die Pfadvariable aufzulösen, schneidet er einfach den Rest der Daten ab, nachdem er auf einen Punkt (.) Am Ende des Uri gestoßen ist. Hinweis: Dies geschieht auch nur, wenn Sie die Pfadvariable am Ende der URL beibehalten.

Betrachten Sie beispielsweise 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) {
        // ...  
    }
}

In der obigen URL firstValue = "gallery.df" und secondValue = "link" das letzte Bit nach dem. wird abgeschnitten, wenn die Pfadvariable interpretiert wird.

Um dies zu verhindern, gibt es zwei Möglichkeiten:

1.) Verwenden eines regulären Ausdrucks

Verwenden Sie am Ende der Zuordnung einen regulären Ausdruck

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

Mit + geben wir an, dass jeder Wert, nachdem der Punkt ebenfalls Teil der Pfadvariablen ist.

2.) Hinzufügen eines Schrägstrichs am Ende unserer @PathVariable

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

Dies schließt unsere zweite Variable ein, die sie vor dem Standardverhalten von Spring schützt.

3) Durch Überschreiben der Standard-Webmvc-Konfiguration von Spring

Spring bietet Möglichkeiten zum Überschreiben der Standardkonfigurationen, die mithilfe der Anmerkungen @EnableWebMvc importiert werden. Wir können die Spring MVC-Konfiguration anpassen, indem wir unsere eigene DefaultAnnotationHandlerMapping- Bean im Anwendungskontext deklarieren und ihre useDefaultSuffixPattern- Eigenschaft auf false setzen. Beispiel:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

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

Beachten Sie, dass das Überschreiben dieser Standardkonfiguration alle URLs betrifft.

Hinweis: Hier erweitern wir die WebMvcConfigurationSupport-Klasse, um die Standardmethoden zu überschreiben. Es gibt noch eine Möglichkeit, die deaktivierten Konfigurationen durch Implementieren der WebMvcConfigurer-Schnittstelle zu überschreiben. Weitere Informationen hierzu finden Sie unter: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

Ananthapadmanabhan
quelle