Spring MVC @PathVariable被截断


142

我有一个控制器,可提供对信息的RESTful访问:

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

我遇到的问题是,如果我使用带有特殊字符的路径变量访问服务器,则会被截断。例如: http:// localhost:8080 / blah-server / blah / get / blah2010.08.19-02:25:47

参数blahName将为blah2010.08

但是,对request.getRequestURI()的调用包含所有传入的信息。

任何想法如何防止Spring截断@PathVariable?


看来这已经在Spring 3.2 M2已经解决:看到允许对指定内容协商有效的文件扩展名的路径它的文档
Arjan 2012年

Answers:


149

尝试对@RequestMapping参数使用正则表达式:

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

1
感谢您的回答,帮我解决了其中的用户名某种程度上得到了修整的情况下。( - :与“useDefaultSuffixPattern”另一种选择是不是一种选择,因为我们使用的不是XML @Configuration春班。
evandongen

3
这可行,但是正则表达式中冒号的意义是什么?
Noah Yetter

6
诺亚,我已经很长时间没有使用它了,但是我认为冒号将正则表达式与参数名称分开以将其绑定到。
Earldouglas 2012年

3
我们有一个类似的问题/item/user@abc.com,@被截断后的任何内容,可以通过添加另一个斜杠/item/user@abc.com/来解决
Titi Wangsa bin Damhore 2014年

59

这可能与SPR-6164密切相关。简而言之,该框架尝试将一些技巧应用到URI解释中,以消除其认为的文件扩展名。这会blah2010.08.19-02:25:47变成的效果blah2010.08,因为它认为.19-02:25:47是文件扩展名。

如链接的问题所述,您可以通过DefaultAnnotationHandlerMapping在应用程序上下文中声明自己的bean并将其useDefaultSuffixPattern属性设置为来禁用此行为false。这将覆盖默认行为,并阻止它破坏您的数据。


3
默认情况下启用基于扩展的内容协商似乎是一个奇怪的选择。实际上有多少个系统以不同的格式公开相同的资源?
Affe

我今天早晨尝试过,但路径变量仍然被截断。
phogel

30
+1是一个不错的答案,也可以使用短语“破坏数据”
Chris Thompson

11
对于Spring 3.1用户-如果您使用的是new RequestMappingHandlerMapping,则要设置的属性为useSuffixPatternMatch(也为false)。@Ted:链接的问题提到在3.2中,他们希望添加更多控制权,因此不必全非。
Nick

2
在Spring 4.2中,这稍微容易配置。我们使用Java配置类,并扩展了WebMvcConfigurationSupport它提供了一个简单的钩子:public void configurePathMatch(PathMatchConfigurer configurer)-覆盖它并设置与您喜欢的方式匹配的路径。
pmckeown

31

Spring认为最后一个点后面的任何东西都是文件扩展名,例如.jsonor,.xml然后将其截断以检索参数。

因此,如果您有/{blahName}

  • /param/param.json/param.xml/param.anything将导致具有值的PARAMparam
  • /param.value.json/param.value.xml否则/param.value.anything将导致带有值的参数param.value

如果将映射更改/{blahName:.+}为建议的值,则任何点(包括最后一个点)都将被视为参数的一部分:

  • /param 会带来有价值的参数 param
  • /param.json 会带来有价值的参数 param.json
  • /param.xml 会带来有价值的参数 param.xml
  • /param.anything 会带来有价值的参数 param.anything
  • /param.value.json 会带来有价值的参数 param.value.json
  • ...

如果您不关心扩展名识别,则可以通过覆盖mvc:annotation-drivenautomagic 来禁用它:

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

因此,如果您有/{blahName}

  • /param/param.json/param.xml/param.anything将导致具有值的PARAMparam
  • /param.value.json/param.value.xml否则/param.value.anything将导致带有值的参数param.value

注意:与默认配置的区别仅在具有的映射时可见/something.{blahName}。请参阅Resthub项目问题

如果要保留扩展管理,从Spring 3.2开始,还可以设置RequestMappingHandlerMapping bean的useRegisteredSuffixPatternMatch属性,以保持激活suffixPattern识别,但仅限于已注册的扩展。

在这里,您仅定义json和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>

请注意,mvc:annotation-driven现在接受contentNegotiation选项以提供自定义bean,但必须将RequestMappingHandlerMapping的属性更改为true(默认为false)(参见https://jira.springsource.org/browse/SPR-7632)。

因此,您仍然必须覆盖所有mvc:annotation驱动的配置。我开了一张去Spring的票,要求自定义RequestMappingHandlerMapping:https : //jira.springsource.org/browse/SPR-11253。如果您有兴趣,请投票。

覆盖时,请谨慎考虑自定义执行管理覆盖。否则,所有自定义Exception映射将失败。您将必须使用list bean重用messageCoverters:

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

我在我参与的开源项目Resthub中实现了针对这些主题的一系列测试:请参阅https://github.com/resthub/resthub-spring-stack/pull/219/fileshttps:// github.com/resthub/resthub-spring-stack/issues/217


16

最后一个点之后的所有内容均被解释为文件扩展名,并且默认情况下已切断。
在您的spring config xml中,您可以添加DefaultAnnotationHandlerMapping并设置useDefaultSuffixPatternfalse(默认值为true)。

因此,打开您的spring xml mvc-config.xml(或称为xml )并添加

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

现在,您的@PathVariable blahName(以及所有其他名称)应该包含全名,包括所有点。

编辑:这是到Spring API链接


我没有尝试过,但是其他人声称您还需要将其删除(<mvc:annotation-driven />如果适用)。
Arjan 2012年

7

我也遇到了同样的问题,并且将该属性设置为false也没有帮助。但是,API表示

请注意,无论如何,包含“ .xxx”后缀或以“ /”结尾的路径都不会使用默认后缀模式进行转换。

我尝试将“ / end”添加到我的RESTful URL中,问题消失了。我对解决方案不满意,但确实有效。

顺便说一句,我不知道Spring设计师在添加此“功能”然后默认将其打开时的想法。恕我直言,应将其删除。


我同意。我最近对此有点感触。
llambda 2014年

7

使用正确的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);
    }
}

这对我来说很棒。运行在Tomcat Spring版本4.3.14
戴夫


3

我只是碰到了这一点,这里的解决方案通常无法按我预期的那样工作。

我建议使用SpEL表达式和多个映射,例如

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

3

仅当参数位于URL的最后一部分时,文件扩展名问题才会存在。更改

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

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

一切都会好起来的-



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>       

3

添加“:。+”对我有用,但是直到我移除了外部花括号为止。

value = {"/username/{id:.+}"} 没用

value = "/username/{id:.+}" 作品

希望我能帮助一个人:]


2

基于Java的配置解决方案,以防止截断(使用未弃用的类):

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

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

更新:

当我使用上述方法时,我意识到Spring Boot自动配置存在一些问题(某些自动配置没有效果)。

相反,我开始使用这种BeanPostProcessor方法。它似乎工作得更好。

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

灵感来源:http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html


2

如果您确定您的文字与任何默认扩展名都不匹配,则可以使用以下代码:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}

1

为了防止Spring MVC @PathVariable被截断,我更可取的解决方案是在path变量的末尾添加斜杠。

例如:

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

因此,请求将如下所示:

http://localhost:8080/api/email/test@test.com/

1

您面临的问题是由于Spring点(。)之后的uri 的最后一部分解释为文件扩展名,如.json或.xml。因此,当spring尝试解析path变量时,它会在uri末尾遇到点(。)之后,将截断其余数据。 注意:仅当您将路径变量保留在uri的末尾时,也会发生这种情况。

例如考虑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) {
        // ...  
    }
}

在上面的网址firstValue =“ gallery.df”和secondValue =“ link”中,。之后的最后一位。在解释路径变量时被截断。

因此,为防止这种情况,有两种可能的方法:

1.)使用正则表达式映射

在映射的末尾使用正则表达式

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

通过使用+,我们指示点后的任何值也将成为path变量的一部分。

2.)在@PathVariable的末尾添加一个斜杠

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

这将包含我们的第二个变量,以保护它不受Spring的默认行为的影响。

3)通过覆盖Spring的默认webmvc配置

Spring提供了一些方法来覆盖通过使用@EnableWebMvc注释导入的默认配置。我们可以通过在应用程序上下文中声明我们自己的DefaultAnnotationHandlerMapping bean并将其useDefaultSuffixPattern属性设置为false来自定义Spring MVC配置。例:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

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

请记住,覆盖此默认配置会影响所有url。

注意:这里我们扩展了WebMvcConfigurationSupport类以覆盖默认方法。通过实现WebMvcConfigurer接口,还有另一种方法来覆盖默认配置。有关此内容的更多信息,请阅读:https : //docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.