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

虚幻大学 xuhss 147℃ 0评论

? 优质资源分享 ?

学习路线指引(点击解锁) 知识定位 人群定位
? Python实战微信订餐小程序 ? 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
?Python量化交易实战? 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

背景

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

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

探索

  1. 参数怎么来的

入口在InvocableHandlerMethod.invokeForRequest,如下:
8146297f69da236ae5d124587c499b68 - 获取Spring中@PathVariable注解里带点的完整参数

是根据PathVariableMethodArgumentResolver.resolveName得来的,如下:
c91975002b2d10c3ef29aa3c782bddc2 - 获取Spring中@PathVariable注解里带点的完整参数
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

什么时候放进attributes里的?,在RequestMappingInfoHandlerMapping.handleMatch,如下:
a39aba5a7454ecc7768dc1d4242d2202 - 获取Spring中@PathVariable注解里带点的完整参数

  1. 参数怎么解析的
程序定义的:/param/{param1} -> /param/{param1}.*
接口传过来的:/param/hehe.hehe

以/分割,第一个字符串param里没有参数,所以会跳过,直接看第二个字符串:
{param1}.* -> pattern=(.*)\Q.\E.*
hehe.hehe

param1=hehe

我们定义的是/param/{param1},怎么就变成了/param/{param1}.*?在PatternsRequestCondition.getMatchingPattern
451b5b71d30f6ca4495e31451633c00e - 获取Spring中@PathVariable注解里带点的完整参数
可以看到如果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 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 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;
 }
 }
 }

转载请注明:xuhss » 获取Spring中@PathVariable注解里带点的完整参数

喜欢 (0)

您必须 登录 才能发表评论!