• 获取Spring中@PathVariable注解里带点的完整参数


    背景

    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();
        }
    }
    
    @RestController
    public class ParamController {
        @GetMapping(value = "/param/{param1}")
        public String param(@PathVariable("param1") String param1) {
            return param1;
        }
    }
    
    @SpringBootApplication
    public 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

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

    原因

    下面的例子都是根据http://127.0.0.1:8080/param/hehe.hehe这个url来调试的

    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.hehe
    
    param1=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. 中间的参数是不受影响的

    @RestController
    public 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. 增加点

    @RestController
    public 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

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

    @RestController
    public 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. 修改参数
    @RestController
    public 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.hehe
    
    param1=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;
    			}
    		}
    	}
    

    参考

    https://www.cnblogs.com/yougatei/p/12812906.html

  • 相关阅读:
    2021 CCPC 桂林站 补题
    2021 ICPC 上海 流水账
    2021 ICPC 沈阳 补题
    vi中的多行注释和取消注释
    查询列表可筛选可模糊查询的写法
    mybatisplus逻辑删除deleted
    @JsonFormat
    @Component类相互引用的加载顺序
    Chrome浏览器嗅探方法
    DataAdapter.FillSchema 方法
  • 原文地址:https://www.cnblogs.com/eaglelihh/p/16273654.html
Copyright © 2020-2023  润新知