带有点(。)的Spring MVC @PathVariable被截断


361

这是问题 Spring MVC @PathVariable被截断的继续

Spring论坛指出,它已作为ContentNegotiationManager的一部分进行了修复(3.2版本)。请参阅下面的链接。
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

在我的应用程序中,带有.com的requestParameter被截断了。

谁能解释我如何使用此新功能?如何在xml上进行配置?

注意:Spring论坛-#1 Spring MVC @PathVariable带有点(。)

Answers:


485

据我所知,这个问题只出现在requestmapping末尾的pathvariable中。

我们可以通过在requestmapping中定义regex插件来解决这一问题。

 /somepath/{variable:.+}

1
谢谢,我认为此修复程序还早于(3.2V之前)可用吗?但是,我不喜欢此修复程序。因为它是在我的应用程序中必须处理的所有URL所需要的……并且将来的URL实现也要加以解决……
Kanagavelu Sugumar

4
这里是我如何解决这个问题,春季3.0.5<!-- Spring Configuration needed to avoid URI using dots to be truncated --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="useDefaultSuffixPattern" value="false" /> </bean>
法里德

11
@Mariusz,语法为{variable_name:regular_expression},因此这里有一个名为的变量variable,该值将使用正则表达式进行匹配.+(其中.“任意字符” +表示“一次或多次”)。
2013年

4
@StefanHaberl如果您variable以常规方式进行匹配,则Spring使用其后缀检测功能并将点后的所有内容都截断。使用regexp匹配时,不会使用这些功能-变量仅与您提供的regexp匹配。
米哈尔Rybak的

9
"variable:.+"当变量中有多个点时,@ martin 不起作用。例如将电子邮件放在像这样的宁静路径的末尾/path/abc@server.com.au。控制器甚至都不会被调用,但是当只有一个点时,它就可以工作/path/abc@server.com。知道为什么和/或解决方法吗?
波希米亚

242

Spring认为最后一个点后面的任何东西都是文件扩展名,例如.json.xml,然后对其进行结构化以检索您的参数。

因此,如果您有/somepath/{variable}

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

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

  • /somepath/param 会带来有价值的参数 param
  • /somepath/param.json 会带来有价值的参数 param.json
  • /somepath/param.xml 会带来有价值的参数 param.xml
  • /somepath/param.anything 会带来有价值的参数 param.anything
  • /somepath/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>

因此,如果您有/somepath/{variable}

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

注意:与默认配置的区别仅在有类似的映射时可见somepath/something.{variable}。请参阅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


原谅我是新手,所以您将bean配置放在哪里?它适用于什么春季版本?
飞溅

@Splash:您必须在“标准” Spring applicationContext.xml文件中定义这些bean。这至少适用于Spring 3.2。大约(至少部分)之前
bmeurant

我认为这是正确的答案。似乎正是针对OP问题引入了参数“ useRegisteredSuffixPatternMatch”。
lrxw

这对我来说只是解决方案的一半。请参阅@Paul Aerer的答案。
8bitjunkie

96

Spring 4更新:从4.0.1开始,您可以使用PathMatchConfigurer(通过WebMvcConfigurer),例如

@Configuration
protected static class AllResources extends WebMvcConfigurerAdapter {

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

}


@Configuration
public class WebConfig implements WebMvcConfigurer {

   @Override
   public void configurePathMatch(PathMatchConfigurer configurer) {
       configurer.setUseSuffixPatternMatch(false);
   }
}

在xml中,它将是(https://jira.spring.io/browse/SPR-10163):

<mvc:annotation-driven>
    [...]
    <mvc:path-matching registered-suffixes-only="true"/>
</mvc:annotation-driven>

11
到目前为止,这是最干净的解决方案:关闭导致它的功能,而不是对其进行破解。我们仍然没有使用此功能,因此问题已解决-完美!
David Lavender 2015年

AllResources类去哪儿了?
irl_irl '16

1
@ste_irl在与您的main相同的包中添加一个Java类。
kometen '16

5
使用matcher.setUseSuffixPatternMatch(false)完全禁用后缀匹配。
Gian Marco Gherardi

这对我来说只是解决方案的一半。请参阅@Paul Aerer的答案。
8bitjunkie

87

除了Martin Frey的答案外,还可以通过在RequestMapping值中添加尾斜杠来解决此问题:

/path/{variable}/

请记住,此修复程序不支持可维护性。现在,它要求所有URI都带有斜线-对于API用户/新开发人员而言,这可能并不明显。因为可能并非所有参数都包含一个.,所以它也可能会造成间歇性错误


2
那甚至是一个更干净的解决方案。我必须找出IE根据后缀设置接受标头的困难方式。所以我想在一些.doc requestmapping上发布,我总是得到下载而不是新的html页面。这种方法解决了这一问题。
Martin Frey 2014年

这是我解决问题的最简单的方法;regexp在许多情况下似乎有点过大了
Riccardo Cossu 2014年

7
但它会与AngularJS的默认行为发生冲突,从而自动删除尾随的斜杠。可以在最新的Angular版本中进行配置,但是如果您不知道发生了什么,则要花几个小时来跟踪它。
dschulten 2014年

1
@dschulten您刚刚节省了我几个小时的调试工作,谢谢!但是,您应该在答案中提到HTPP请求中将要求使用斜杠。
霍夫曼2014年

1
这非常危险!我当然不会推荐它,因为任何实现该API的人都不会期望它。非常不可维护。
sparkyspider

32

在Spring Boot Rest Controller中,我已通过以下步骤解决了这些问题:

RestController:

@GetMapping("/statusByEmail/{email:.+}/")
public String statusByEmail(@PathVariable(value = "email") String email){
  //code
}

从其他客户那里:

Get http://mywebhook.com/statusByEmail/abc.test@gmail.com/

2
该答案取决于斜杠才能起作用。
8bitjunkie

2
就像一个魅力(也没有尾随斜杠)。谢谢!
AFE

27

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

值= { “ /用户名/{id:.+}” } 不起作用

值=“ /用户名/{id:.+}” 有效

希望我能帮助一个人:)


那是因为大括号评估了RegEx,您已经有了一些知识id
8bitjunkie

15

/somepath/{variable:.+}在Java requestMapping标签中工作。


我喜欢这个答案,因为它没有显示出什么无效。
johnnieb

不适用于带有多个点的电子邮件地址。
8bitjunkie

1
@ 8bitjunkie Sth像"/{code:.+}"许多点一样工作,而不是一个,也就是说,61.12.7它也可以工作,例如k.a.p@o.i.n
试试Hard

13

这是一种完全依赖于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 MvcConfig extends WebMvcConfigurationSupport{

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        handlerMapping.setUseTrailingSlashMatch(false);
        return handlerMapping;
    }
}

谢谢,为我解决了。另外,它非常干净明确。+1
bkis

11

解决此问题的一种非常简单的方法是在末尾添加斜线...

例如:

采用 :

/somepath/filename.jpg/

代替:

/somepath/filename.jpg

11

在Spring Boot中,正则表达式解决了类似的问题

@GetMapping("/path/{param1:.+}")

请注意,这仅适用于一个点。它不适用于电子邮件地址。
8bitjunkie

1
@ 8bitjunkie Sth "/{code:.+}"可以为许多点工作,而不是一个,也就是说,61.12.7它也可以工作,例如k.a.p@o.i.n
试用Hard

1
@ 8bitjunkie我已经使用IP地址对其进行了测试。它运作良好。因此,这意味着它适用于多个点。
Dapper Dan

6

在spring 4.2的路径名中包括电子邮件地址的完整解决方案是

<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
    content-negotiation-manager="contentNegotiationManager">
    <mvc:path-matching suffix-pattern="false" registered-suffixes-only="true" />
</mvc:annotation-driven>

将此添加到application-xml


Upvote-这是唯一的答案,它清楚地表明ContentNegotiationManagerFactoryBean和contentNegotiationManager配置项都是必需的
8bitjunkie

5

如果您使用的是Spring 3.2.x和<mvc:annotation-driven />,请创建以下代码BeanPostProcessor

package spring;

public final class DoNotTruncateMyUrls implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RequestMappingHandlerMapping) {
            ((RequestMappingHandlerMapping)bean).setUseSuffixPatternMatch(false);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

然后将其放在您的MVC配置xml中:

<bean class="spring.DoNotTruncateMyUrls" />

与ContentNegotiationManager有关吗?
卡纳加维卢·苏古玛

我的代码仅配置RequestMappingHandlerMapping,以便URL不会被截断。ContentNegotiationManager是另一种野兽。
Jukka

2
这是旧的,但你真的不需要BeanPostProcessor这个。如果使用WebMvcConfigurationSupport,则可以覆盖该requestMappingHandlerMapping @Bean方法。如果使用XML配置,则可以声明自己的RequestMappingHandlerMappingbean并声明该属性。
Sotirios Delimanolis

非常感谢,对于相同的问题,我尝试了多种解决方案,仅此一种对我有用。:-)
我们是博格2014年

3

最后,我在Spring Docs中找到了解决方案:

若要完全禁用文件扩展名,必须设置以下两项:

 useSuffixPatternMatching(false), see PathMatchConfigurer

 favorPathExtension(false), see ContentNegotiationConfigurer

将其添加到我的WebMvcConfigurerAdapter实现中可以解决问题:

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

@Override
public void configurePathMatch(PathMatchConfigurer matcher) {
    matcher.setUseSuffixPatternMatch(false);
}

2

对我来说

@GetMapping(path = "/a/{variableName:.+}")

确实有效,但是仅当您还将请求URL中的“点”也编码为“%2E”时,它才有效。但要求URL全部都是……虽然有效,但这不是“标准”编码。感觉像是一个bug:|

类似于“尾随斜杠”的另一种解决方法是移动将带有点“ inline”的变量,例如:

@GetMapping(path =“ / {variableName} / a”)

现在将保留所有点,无需修改或正则表达式。


1

从Spring 5.2.4(Spring Boot v2.2.6.RELEASE)开始 PathMatchConfigurer.setUseSuffixPatternMatchContentNegotiationConfigurer.favorPathExtension已弃用(https://spring.io/blog/2020/03/24/spring-framework-5-2-5-available-nowhttps://github.com/spring-projects/spring-framework/issues/24179)。

真正的问题是客户端请求特定的媒体类型(例如.com),并且Spring默认情况下添加了所有这些媒体类型。在大多数情况下,您的REST控制器只会生成JSON,因此不支持请求的输出格式(.com)。为了克服这个问题,您应该通过更新rest控制器(或特定方法)以支持“输出”格式(@RequestMapping(produces = MediaType.ALL_VALUE)来保持良好状态,并且当然允许使用点号({username:.+})之。

例:

@RequestMapping(value = USERNAME, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class UsernameAPI {

    private final UsernameService service;

    @GetMapping(value = "/{username:.+}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.ALL_VALUE)
    public ResponseEntity isUsernameAlreadyInUse(@PathVariable(value = "username") @Valid @Size(max = 255) String username) {
        log.debug("Check if username already exists");
        if (service.doesUsernameExist(username)) {
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }
        return ResponseEntity.notFound().build();
    }
}

Spring 5.3及更高版本将仅匹配注册的后缀(媒体类型)。

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.