1.概述
在本文中,我们将讨论使用Spring MVC 时的一个常见问题。
当使用带有@RequestMapping
的路径变量(@PathVariable) 来映射URI末尾时,如果路径变量包含点,
我们获取的路径变量的内容会在最后一个点处被截断。下面我们就来研究一下原因以及解决方法。
2. Spring MVC的后缀模式匹配
Spring MVC由于解析路径变量的方式而导致这种通常不想要的行为。
Spring MVC 默认执行“.*
”后缀匹配,所以一个映射到“/person
”的控制器隐式地映射到“/person.*
”。
这让请求通过URL路径(比如/person.pdf
,/person.xml
)来获取资源很简单。
具体地说,Spring认为最后一个点后面的任何东西都是文件扩展名,例如 .pdf或 .xml。
让我们看一个使用路径变量的示例,然后使用不同的可能值来分析结果:
# curl http://localhost:8080/dpv/link/ripjava
firstValue: link , secondValue: ripjava%
# curl http://localhost:8080/dpv/link.com/ripjava.java
firstValue: link.com , secondValue: ripjava%
# curl http://localhost:8080/dpv/link.com/ripjava.java.com
firstValue: link.com , secondValue: ripjava.java
测试可以发现,第一个变量不受影响,但第二个的最后一个点之后的内容总是被截断。
3. 解决方法
3.1 使用正则表达式
一种方法是通过添加正则表达式映射来修改我们的@PathVariable
定义。
这样的话,任何的点(包括最后一个点)都将被视为我们参数的一部分:
@RequestMapping(value = "/dpv2/{firstValue}/{secondValue:.+}", method = RequestMethod.GET)
@ResponseBody
public String dotPathVariable2(@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
return String.format("firstValue: %s , secondValue: %s", firstValue, secondValue);
}
我们来测试一下:
# curl http://localhost:8080/dpv2/link/ripjava
firstValue: link , secondValue: ripjava%
# curl http://localhost:8080/dpv2/link.com/ripjava.java.com
firstValue: link.com , secondValue: ripjava.java.com%
# curl http://localhost:8080/dpv2/link.com/ripjava.java.com
firstValue: link.com , secondValue: ripjava.java.com%
3.2 在@PathVariable的末尾添加斜杠
避免此问题的另一种方法是在@PathVariable
的末尾添加斜杠。这将包含我们的第二个变量,以保护它免受Spring的默认行为的影响:
@RequestMapping(value = "/dpv3/{firstValue}/{secondValue}/", method = RequestMethod.GET)
需要注意的是,我们需要在每个URI末尾添加斜杠。
我们来测试一下:
# curl http://localhost:8080/dpv3/link.com/ripjava.java/
firstValue: link.com , secondValue: ripjava.java%
# curl http://localhost:8080/dpv3/link.com/ripjava.java
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 404 Not Found</title>
</head>
<body><h2>HTTP ERROR 404 Not Found</h2>
<table>
<tr><th>URI:</th><td>/dpv3/link.com/ripjava.java</td></tr>
<tr><th>STATUS:</th><td>404</td></tr>
<tr><th>MESSAGE:</th><td>Not Found</td></tr>
<tr><th>SERVLET:</th><td>mvc</td></tr>
</table>
<hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.24.v20191120</a><hr/>
</body>
</html>
3.3 全局修改
上面的两个解决方法适用于单个的请求映射。
如果我们需要更改全局的行为,可以通过在应用程序上下文中声明我们自己的DefaultAnnotationHandlerMapping
bean并将其useDefaultSuffixPattern
属性设置为false来自定义Spring MVC配置:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.ripjava.spring.mvc.model"})
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseSuffixPatternMatch(false);
}
}
需要注意的是,这种方法会影响所有URL。
我们重新测试一下我们在第2节中的URL:
# curl http://localhost:8080/dpv/link/ripjava
firstValue: link , secondValue: ripjava%
# curl http://localhost:8080/dpv/link.com/ripjava.java
firstValue: link.com , secondValue: ripjava.java%
# curl http://localhost:8080/dpv/link.com/ripjava.java.com
firstValue: link.com , secondValue: ripjava.java.com%
4. 结论
在本文中,我们探索了在Spring MVC中使用@PathVariable
和@RequestMapping
时解决路径变量映射URL末尾时点被截取的问题以及原因。
与往常一样,可以在GitHub上找到示例实现。