当商品编码遇到了.号, spring restful @PathVariable 应对措施

1. 背景

今天我们的某个大型商城做UAT, 客户在后台创建了个 商品code 是 0900-PK.3.58-4-A004 的商品, 然后界面可以搜索到, 但是点击进入明细页面报错了

当商品编码遇到了.号, spring restful @PathVariable 应对措施

分析logback 日志, 发现了异常, 以及一个怪怪的 商品code

0900-PK.3.58-4-A004 到了controller 层,变成了 0900-PK.3

当商品编码遇到了.号, spring restful @PathVariable 应对措施

检查 Controller 中的 rrequestMapping , 感觉没毛病

当商品编码遇到了.号, spring restful @PathVariable 应对措施

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 中值 已经被解析成 当商品编码遇到了.号, spring restful @PathVariable 应对措施

storeId=2, itemCode=0900-PK.3

那顺藤摸瓜,往上走

当商品编码遇到了.号, spring restful @PathVariable 应对措施

发现原来的 路径格式是 /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:path-matching suffix-pattern="false" />

<mvc:annotation-driven>

        <!-- 
        add by feilong ,do with http://127.0.0.1:8091/item/2/0900-PK.3.58-4-A004 
        see http://jira.baozun.cn/browse/BBFED-3045
        -->
        <mvc:path-matching suffix-pattern="false" />
...

如果该特性, since spring 3.1

当商品编码遇到了.号, spring restful @PathVariable 应对措施

方案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