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

时间:2013-05-02 06:54:59

标签: spring rest spring-mvc spring-annotations

这是问题的延续 Spring MVC @PathVariable getting truncated

Spring论坛声明它已修复(3.2版本)作为ContentNegotiationManager的一部分。请参阅以下链接 https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

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

有人能解释我如何使用这个新功能吗?它是如何在xml中配置的?

注意:spring forum- #1 Spring MVC @PathVariable with dot (.) is getting truncated

18 个答案:

答案 0 :(得分:442)

据我所知,此问题仅出现在请求映射结束时的path变量上。

我们能够通过在请求映射中定义正则表达式插件来解决这个问题。

 /somepath/{variable:.+}

答案 1 :(得分:226)

Spring认为最后一个点后面的任何内容都是文件扩展名,例如.json.xml,并将其设置为检索您的参数。

所以如果你有/somepath/{variable}

  • /somepath/param/somepath/param.json/somepath/param.xml/somepath/param.anything会产生价值为param
  • 的参数
  • /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-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>

所以,再次,如果你有/somepath/{variable}

  • /somepath/param/somepath/param.json/somepath/param.xml/somepath/param.anything会产生价值为param
  • 的参数
  • /somepath/param.value.json/somepath/param.value.xml/somepath/param.value.anything将生成一个值为param.value
  • 的参数

注意:只有当您有somepath/something.{variable}之类的映射时,才能看到与默认配置的差异。见Resthub project issue

如果你想保持扩展管理,从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。如果你在白色,请投票。

虽然压倒一切,但还要考虑自定义执行管理覆盖。否则,所有自定义异常映射都将失败。您必须将messageCoverters与list bean重用:

<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/files&amp; https://github.com/resthub/resthub-spring-stack/issues/217

答案 2 :(得分:90)

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

@Configuration
protected static class AllResources extends WebMvcConfigurerAdapter {

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

}

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

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

答案 3 :(得分:84)

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

/path/{variable}/

请注意,此修复程序不支持可维护性。它现在要求所有URI都有一个尾部斜杠 - 这对API用户/新开发人员来说可能并不明显。因为可能并非所有参数都包含.,所以它也可能会产生间歇性错误

答案 4 :(得分:25)

添加&#34;:。+&#34;为我工作,但直到我删除外部花括号。

值= {&#34; /用户名/ {id:。+}&#34; } 无法正常工作

value =&#34; / username / {id:。+}&#34;的工作原理

希望我能帮助某人:)

答案 5 :(得分:25)

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

RestController:

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

来自Rest Client:

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

答案 6 :(得分:14)

/somepath/{variable:.+}适用于Java requestMapping标记。

答案 7 :(得分:12)

这是一种完全依赖于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;
    }
}

答案 8 :(得分:10)

解决此问题的一个非常简单的方法是附加一个尾部斜杠......

e.g:

使用:

/somepath/filename.jpg/

而不是:

/somepath/filename.jpg

答案 9 :(得分:9)

在Spring Boot中,正则表达式解决了像

这样的问题
@GetMapping("/path/{param1:.+}")

答案 10 :(得分:6)

包含弹簧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

答案 11 :(得分: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 config xml:

<bean class="spring.DoNotTruncateMyUrls" />

答案 12 :(得分:2)

从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及更高版本将仅匹配已注册的后缀(媒体类型)。

答案 13 :(得分:1)

最后,我在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);
}

答案 14 :(得分:0)

对我来说

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

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

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

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

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

答案 15 :(得分:0)

如果同时写后端和前端,则另一个简单的解决方案是在URL的末尾附加一个“ /”。如果是这样,则无需更改后端...

somepath/myemail@gmail.com/

开心!

答案 16 :(得分:0)

如果您使用的是Spring 3.2+,则以下解决方案将有所帮助。这样处理所有URL绝对比在请求URI映射中允许使用regex模式更好。像/somepath/{variable:.+}

在xml文件中定义一个bean

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="useSuffixPatternMatch" value="false"/>
        <property name="useRegisteredSuffixPatternMatch" value="true"/>
    </bean>

标志用法可以在文档中找到。我很想解释

据说 useRegisteredSuffixPatternMatch

解决方法已解决了该问题。来自该类中的Java文档

如果启用,则映射到“ / users”的控制器方法也将与 “ /users.json”,假设“ .json”是在 提供了{@link #setContentNegotiationManager(ContentNegotiationManager) contentNegotiationManager}。这对于仅允许特定 要使用的URL扩展名以及“。”的情况在网址路径中 可能导致路径变量内容的模棱两可的解释,例如 “ / users / {user}”以及传入的URL,例如“ /users/john.j.joe”和 “ /users/john.j.joe.json”)。

答案 17 :(得分:0)

简单的解决方案修复:在@RequestMapping 中添加一个正则表达式 {q:.+}

[Costs]
DriverUnit=DBXDynalink
DriverPackageLoader=TDBXDynalinkDriverLoader
DriverAssemblyLoader=Borland.Data.TDBXDynalinkDriverLoader
MetaDataPackageLoader=TDBXOracleMetaDataCommandFactory,DbxOracleDriver260.bpl
MetaDataAssemblyLoader=Borland.Data.TDBXOracleMetaDataCommandFactory,Borland.Data.DbxOracleDriver,Version=24.0.0.0,Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b
GetDriverFunc=getSQLDriverORACLE
LibraryName=dbxora.dll
LibraryNameOsx=libsqlora.dylib
VendorLib=oci.dll
VendorLibWin64=oci.dll
VendorLibOsx=libociei.dylib
Database=ORACLE
User_Name=costs
Password=costs
MaxBlobSize=-1
LocaleCode=0000
IsolationLevel=ReadCommitted
RowsetSize=20
OSAuthentication=False
MultipleTransactions=False
TrimChar=False
BlobSize=-1
ErrorResourceFile=
OS Authentication=False
Multiple Transaction=False
Trim Char=False
Decimal Separator=,

现在,对于输入 /site/jamesmedice.com,“site”将显示正确的 james'site