关于Spring中的useSuffixPatternMatch

博客 分享
0 175
张三
张三 2022-05-15 18:59:18
悬赏:0 积分 收藏

关于Spring中的useSuffixPatternMatch

通过URL的后缀发现了Spring中的useSuffixPatternMatch这个参数

背景

spring-boot的版本是2.1.4.RELEASE,spring的版本是5.1.6.RELEASE

一个例子如下:

@Configuration@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)@SuppressWarnings("unchecked")public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {    @Override    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {        return new RequestMappingHandlerMapping();    }}@RestControllerpublic class ParamController {    @GetMapping(value = "/param/{param1}")    public String param(@PathVariable("param1") String param1) {        return param1;    }}@SpringBootApplicationpublic class MyApplication {    public static void main(String[] args) {        SpringApplication.run(MyApplication.class, args);    }}

启动一下,访问http://127.0.0.1:8080/param/hehehttp://127.0.0.1:8080/param/hehe.hehe都返回hehe

如果访问http://127.0.0.1:8080/param/hehe.hehe.hehe,它会返回hehe.hehe

所以会发现它把最后一个小数点后面的字符给截掉了,那如果我们想要获取完整的字符串,该怎么办呢?

探索

  1. 参数怎么来的

入口在InvocableHandlerMethod.invokeForRequest,如下:

是根据PathVariableMethodArgumentResolver.resolveName得来的,如下:

HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

什么时候放进attributes里的?,在RequestMappingInfoHandlerMapping.handleMatch,如下:

  1. 参数怎么解析的
程序定义的:/param/{param1} -> /param/{param1}.*接口传过来的:/param/hehe.hehe以/分割,第一个字符串param里没有参数,所以会跳过,直接看第二个字符串:{param1}.* -> pattern=(.*)\Q.\E.*hehe.heheparam1=hehe

我们定义的是/param/{param1},怎么就变成了/param/{param1}.*?在PatternsRequestCondition.getMatchingPattern

可以看到如果useSuffixPatternMatch为true,并且指定的url里没有.,会在后缀自动增加.*

  1. useSuffixPatternMatch是在哪里设置的?
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping		implements MatchableHandlerMapping, EmbeddedValueResolverAware {	private boolean useSuffixPatternMatch = true;	@Override	public void afterPropertiesSet() {		this.config = new RequestMappingInfo.BuilderConfiguration();		this.config.setUrlPathHelper(getUrlPathHelper());		this.config.setPathMatcher(getPathMatcher());		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); // 这里设置了		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);		this.config.setContentNegotiationManager(getContentNegotiationManager());		super.afterPropertiesSet();	}}

解决

  1. 修改WebConfiggetRequestMappingHandlerMapping
@Configuration@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)@SuppressWarnings("unchecked")public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {    @Override    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {        RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);        return requestMappingHandlerMapping;    }}
  1. 增加configurePathMatch方法
@Configuration@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)@SuppressWarnings("unchecked")public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {    @Override    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {        return new RequestMappingHandlerMapping();    }    @Override    public void configurePathMatch(PathMatchConfigurer configurer) {        configurer.setUseSuffixPatternMatch(false);    }}
  1. url中增加点

a. 中间的参数是不受影响的

@RestControllerpublic class ParamController {    @GetMapping(value = "/param/{param1}/{param2}")    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {        return param1 + " " + param2;    }}

访问http://127.0.0.1:8080/param/hehe.hehe/hehe.hee返回hehe.hehe hehe

b. 增加点

@RestControllerpublic class ParamController {    //@GetMapping(value = "/param/{param1}")    //public String param(@PathVariable("param1") String param1) {    //    return param1;    //}    @GetMapping(value = "/param/{param1}.{param2}")    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {        return param1 + " " + param2;    }}

访问http://127.0.0.1:8080/param/hehe.hehe返回hehe hehe

注意第一个方法和第二个方法不要同时出现,如果同时出现的话,则会访问第一个方法

@RestControllerpublic class ParamController {    @GetMapping(value = "/param/{param1}")    public String param(@PathVariable("param1") String param1) {        return param1;    }    @GetMapping(value = "/param/{param1}.{param2}")    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {        return param1 + " " + param2;    }}

访问http://127.0.0.1:8080/param/hehe.heh返回hehe

  1. 修改参数
@RestControllerpublic class ParamController {    @GetMapping(value = "/param/{param1:.+}")    public String param(@PathVariable("param1") String param1) {        return param1;    }}

访问http://127.0.0.1:8080/param/hehe.hehe,返回hehe.hehe

原因如下:

/param/{param1:.+} -> /param/{param1:.+}/param/hehe.hehe{param1:.+} -> pattern=(.+)hehe.heheparam1=hehe.hehe
得到pattern以及提取参数的类 AntPathStringMatcher	protected static class AntPathStringMatcher {		private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");		private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";		private final Pattern pattern;		private final List<String> variableNames = new LinkedList<>();		public AntPathStringMatcher(String pattern) {			this(pattern, true);		}		public AntPathStringMatcher(String pattern, boolean caseSensitive) {			StringBuilder patternBuilder = new StringBuilder();			Matcher matcher = GLOB_PATTERN.matcher(pattern);			int end = 0;			while (matcher.find()) {				patternBuilder.append(quote(pattern, end, matcher.start()));				String match = matcher.group();				if ("?".equals(match)) {					patternBuilder.append('.');				}				else if ("*".equals(match)) {					patternBuilder.append(".*");				}				else if (match.startsWith("{") && match.endsWith("}")) {					int colonIdx = match.indexOf(':');					if (colonIdx == -1) {						patternBuilder.append(DEFAULT_VARIABLE_PATTERN);						this.variableNames.add(matcher.group(1));					}					else {						String variablePattern = match.substring(colonIdx + 1, match.length() - 1);						patternBuilder.append('(');						patternBuilder.append(variablePattern);						patternBuilder.append(')');						String variableName = match.substring(1, colonIdx);						this.variableNames.add(variableName);					}				}				end = matcher.end();			}			patternBuilder.append(quote(pattern, end, pattern.length()));			this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :					Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));		}		private String quote(String s, int start, int end) {			if (start == end) {				return "";			}			return Pattern.quote(s.substring(start, end));		}		/**		 * Main entry point.		 * @return {@code true} if the string matches against the pattern, or {@code false} otherwise.		 */		public boolean matchStrings(String str, @Nullable Map<String, String> uriTemplateVariables) {			Matcher matcher = this.pattern.matcher(str);			if (matcher.matches()) {				if (uriTemplateVariables != null) {					// SPR-8455					if (this.variableNames.size() != matcher.groupCount()) {						throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +								this.pattern + " does not match the number of URI template variables it defines, " +								"which can occur if capturing groups are used in a URI template regex. " +								"Use non-capturing groups instead.");					}					for (int i = 1; i <= matcher.groupCount(); i++) {						String name = this.variableNames.get(i - 1);						String value = matcher.group(i);						uriTemplateVariables.put(name, value);					}				}				return true;			}			else {				return false;			}		}	}
posted @ 2022-05-15 18:24 eaglelihh 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员