这是问题 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
据我所知,此问题仅出现在请求映射末尾的路径变量中。
我们能够通过在请求映射中定义正则表达式插件来解决这个问题。
/somepath/{variable:.+}
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-driven 配置。我打开了一张 Spring 票,要求提供自定义 RequestMappingHandlerMapping :https://jira.springsource.org/browse/SPR-11253。如果您有兴趣,请投票。
在覆盖时,要小心考虑自定义执行管理覆盖。否则,您的所有自定义异常映射都将失败。您将不得不重用带有列表 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>
matcher.setUseSuffixPatternMatch(false)
完全禁用后缀匹配。
除了 Martin Frey 的回答,这也可以通过在 RequestMapping 值中添加斜杠来解决:
/path/{variable}/
请记住,此修复不支持可维护性。它现在要求所有 URI 都有一个尾部斜杠——这对于 API 用户/新开发人员来说可能并不明显。由于可能并非所有参数都包含 .
,因此也可能会产生间歇性错误
在 Spring Boot Rest Controller 中,我通过以下步骤解决了这些问题:
休息控制器:
@GetMapping("/statusByEmail/{email:.+}/")
public String statusByEmail(@PathVariable(value = "email") String email){
//code
}
从休息客户端:
Get http://mywebhook.com/statusByEmail/abc.test@gmail.com/
添加“:。+”对我有用,但直到我删除了外大括号。
value = {"/username/{id:.+}"} 不起作用
value = "/username/{id:.+}" 有效
希望我帮助了某人:)
id
/somepath/{variable:.+}
适用于 Java requestMapping
标记。
"/{code:.+}"
适用于许多点而不是一个即 61.12.7
它也适用于即 k.a.p@o.i.n
这是一种完全依赖于 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;
}
}
解决此问题的一种非常简单的方法是附加斜杠...
例如:
利用 :
/somepath/filename.jpg/
代替:
/somepath/filename.jpg
在 Spring Boot 中,正则表达式解决了类似的问题
@GetMapping("/path/{param1:.+}")
"/{code:.+}"
适用于许多点而不是一个即 61.12.7
它也适用于即 k.a.p@o.i.n
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
如果您使用 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" />
BeanPostProcessor
。如果您使用 WebMvcConfigurationSupport
,您可以覆盖 requestMappingHandlerMapping
@Bean
方法。如果您使用 XML 配置,您只需声明自己的 RequestMappingHandlerMapping
bean 并声明该属性。
最后我在 Spring Docs 中找到了解决方案:
要完全禁用文件扩展名,您必须同时设置以下两项: useSuffixPatternMatching(false),请参阅 PathMatchConfigurer 青睐PathExtension(false),请参阅 ContentNegotiationConfigurer
将此添加到我的 WebMvcConfigurerAdapter
实现中解决了该问题:
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
@Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseSuffixPatternMatch(false);
}
对我来说
@GetMapping(path = "/a/{variableName:.+}")
确实有效,但前提是您还将请求 url 中的“点”编码为“%2E”,然后它才有效。但是要求所有的 URL 都是……这不是“标准”编码,尽管有效。感觉像是一个错误:|
另一种解决方法,类似于“尾部斜杠”方式是移动将具有点“内联”的变量例如:
@GetMapping(path = "/{variableName}/a")
现在所有点都将被保留,无需修改。
从 Spring 5.2.4 (Spring Boot v2.2.6.RELEASE) 开始,PathMatchConfigurer.setUseSuffixPatternMatch
和 ContentNegotiationConfigurer.favorPathExtension
已被弃用(https://spring.io/blog/2020/03/24/spring-framework-5-2-5-available-now 和 https://github.com/spring-projects/spring-framework/issues/24179)。
真正的问题是客户端请求特定的媒体类型(如 .com),而 Spring 默认添加了所有这些媒体类型。在大多数情况下,您的 REST 控制器只会生成 JSON,因此它不支持请求的输出格式 (.com)。为了克服这个问题,你应该通过更新你的休息控制器(或特定方法)来支持'输出'格式(@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 映射中应用正则表达式模式来允许更好。像/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 doc
如果启用,映射到“/users”的控制器方法也匹配“/users.json”,假设“.json”是使用提供的 {@link #setContentNegotiationManager(ContentNegotiationManager) contentNegotiationManager} 注册的文件扩展名。这对于只允许使用特定的 URL 扩展名以及使用“.”的情况很有用。在 URL 路径中可能导致对路径变量内容的模糊解释(例如,给定“/users/{user}”和传入的 URL,例如“/users/john.j.joe”和“/users/john.j.joe” .json”)。
简单的解决方案修复:在 @RequestMapping 中添加正则表达式 {q:.+}
@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");
}
}
现在,对于输入 /site/jamesmedice.com,“site”将显示正确的 james'site
<!-- 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>
中解决问题的方法{variable_name:regular_expression}
,所以这里我们有一个名为variable
的变量,该值将使用正则表达式.+
匹配(其中.
表示“任何字符”,+
表示“一次或多次')。variable
,Spring 将使用其后缀检测功能并截断点后的所有内容。当您使用正则表达式匹配时,不会使用这些功能 - 变量仅与您提供的正则表达式匹配。"variable:.+"
不起作用。例如,将电子邮件放在/path/abc@server.com.au
等宁静路径的末尾。控制器甚至没有被调用,但是当只有一个点/path/abc@server.com
时它可以工作。知道为什么和/或解决方法吗?