Spring MVC @PathVariable带点(.)会被截断

这是问题的延续 Spring MVC @PathVariable被截断 < / p >

Spring论坛声明它已经固定(3.2版本)作为ContentNegotiationManager的一部分。参见下面的链接 https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632 < / p >

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

谁能告诉我如何使用这个新功能?如何在xml中配置它?

注:春季论坛- #1 Spring MVC @PathVariable with dot(.)被截断

172214 次浏览

据我所知,这个问题只出现在请求映射结束时的路径变量。

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

 /somepath/{variable:.+}

如果您正在使用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" />

除了Martin Frey的回答,这个问题也可以通过在RequestMapping值中添加一个尾随斜杠来修复:

/path/{variable}/

请记住,此修复程序不支持可维护性。现在它要求所有的URI后面都有一个斜杠——这对API用户/新开发人员来说可能不太明显。因为可能不是所有的参数都有.,它也可能会产生间歇性的错误

Spring认为最后一个点后面的任何东西都是一个文件扩展名,例如__abc0或.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项目问题

如果你想保持扩展管理,从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) (cf. https://jira.springsource.org/browse/SPR-7632)。

出于这个原因,您仍然必须重写所有mvc:注解驱动的配置。我打开了一个Spring请求自定义RequestMappingHandlerMapping: https://jira.springsource.org/browse/SPR-11253的票据。如果你感兴趣,请投票。

在重写时,也要注意考虑自定义执行管理重写。否则,所有自定义Exception映射都将失败。你必须使用列表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/files &https://github.com/resthub/resthub-spring-stack/issues/217

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>

解决这个问题的一个非常简单的方法是在后面添加一个斜杠…

例如:

使用:

/somepath/filename.jpg/

而不是:

/somepath/filename.jpg

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

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

将其添加到应用程序xml中

添加“:”。“+”对我来说很有用,但直到我去掉了外花括号。

value = "/username/{id:。+}” 没有工作

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

希望我能帮到别人:)

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

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

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

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

RestController:

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

从Rest客户端:

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

对我来说

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

这是可行的,但前提是你也将"。quot;在您的请求url中作为"%2E"这样就有用了。但要求URL都是…这不是一个“标准”;编码,虽然有效。感觉像是一个bug:|

另一种方法,类似于后面的斜杠。方法是移动变量,将有点“;内联”;例:

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

现在所有的点都将被保留,不需要修改。

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

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

 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);
}
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控制器(或特定的方法)来支持' output '格式(@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及以上版本将只匹配已注册的后缀(媒体类型)。

如果前后都写,另一个简单的解决方法是在后面加上"在前面的URL的末尾。如果是这样,你不需要改变你的后端…

somepath/myemail@gmail.com/

是快乐!

如果您正在使用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文档

如果启用,一个控制器方法映射到"/user "也匹配于 “/ users.json"假设“.json"文件扩展名是否已注册到 提供{@link #setContentNegotiationManager(ContentNegotiationManager) contentNegotiationManager}。这对于只允许特定的 使用URL扩展名以及在";在URL路径中 可能导致路径变量内容的模糊解释,(例如给定 “/用户/ {user}“;和输入url,如"/users/john.j.joe"而且 “/用户/ john.j.joe.json")。< / p >

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

@RequestMapping("medici/james/Site")
public class WebSiteController {


@RequestMapping(value = "/{site:.+}", method = RequestMethod.GET)
public ModelAndView display(@PathVariable("site") String site) {
return getModelAndView(site, "web site");


}
}

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