1. 背景
今天我们的某个大型商城做UAT, 客户在后台创建了个 商品code 是 0900-PK.3.58-4-A004
的商品, 然后界面可以搜索到, 但是点击进入明细页面报错了
分析logback 日志, 发现了异常, 以及一个怪怪的 商品code
0900-PK.3.58-4-A004
到了controller 层,变成了 0900-PK.3
检查 Controller 中的 rrequestMapping , 感觉没毛病
2. 排查
事务反常必有妖, 老司机遇到了新问题, 我们做过了那么多的官方商城, 商品code 通常都是规整的, 比如 ABC1234, 第一次遇到 0900-PK.3.58-4-A004
的商品Code
OK, let go, 捉妖去~~
源码面前,了无秘密, 开启debug 大法
对于@PathVariable 的参数, 值其实是解析之后,转成map 存放在 request 作用域中, name是 常量 org.springframework.web.servlet.HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
当我去看这个属性值的时候, 发现 uriTemplateVars map 中值 已经被解析成
storeId=2, itemCode=0900-PK.3
那顺藤摸瓜,往上走
发现原来的 路径格式是 /item/{storeId}/{itemCode}
,变成了 /item/{storeId}/{itemCode}.*
好神奇
再往上走 到了 org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getMatchingPattern(String, String)
private String getMatchingPattern(String pattern, String lookupPath) {
if (pattern.equals(lookupPath)) {
return pattern;
}
if (this.useSuffixPatternMatch) {
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
for (String extension : this.fileExtensions) {
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
return pattern + extension;
}
}
}
else {
boolean hasSuffix = pattern.indexOf('.') != -1;
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
return pattern + ".*";
}
}
}
if (this.pathMatcher.match(pattern, lookupPath)) {
return pattern;
}
if (this.useTrailingSlashMatch) {
if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
return pattern +"/";
}
}
return null;
}
表示, 如果 开启了 useSuffixPatternMatch (默认值是 true) ,且路径中有 .号, 将会把. 后面的部分解析为 扩展名
因此就变成了 最后面一个 . 之前的是 商品code ,这就导致了 0900-PK.3.58-4-A004
到了controller 层,变成了 0900-PK.3
如何解决呢?
3. 解决方案
方案A: 全局统一处理
直接在 mvc:annotation-driven 加入
<mvc:annotation-driven>
<mvc:path-matching suffix-pattern="false" />
...
如果该特性, since spring 3.1
方案B
可以在 pdp controller 中把路径改成 /item/{storeId}/{itemCode:.+}
方案C
新建个单独的类 MvcConfig
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;
}
}
4. 参考
https://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated
https://blog.csdn.net/ruipheng/article/details/65438703