7

#yyds干货盘点#【Spring专题】「技术原理」为大家介绍一下Spring中的Ant路径匹配工具...

 2 years ago
source link: https://blog.51cto.com/alex4dream/4909006
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

#yyds干货盘点#【Spring专题】「技术原理」为大家介绍一下Spring中的Ant路径匹配工具组件AntPathMatcher

推荐 原创

浩宇天尚 2022-01-11 12:40:33 博主文章分类:Spring技术专题系列 ©著作权

文章标签 spring html 404错误 文章分类 Java 编程语言 阅读数432

Spring中的绝大多数的路径匹配规则是依照Ant的标准来的

实际上不只是SpringMVC,整个Spring框架的路径解析都是按照Ant的风格来的,在Spring中的具体实现,详情参见 org.springframework.util.AntPathMatcher,具体规则如下

/**
* {@link PathMatcher} implementation for Ant-style path patterns.
*
* <p>Part of this mapping code has been kindly borrowed from <a href="http://ant.apache.org">Apache Ant</a>.
*
* <p>The mapping matches URLs using the following rules:<br>
* <ul>
* <li>{@code ?} matches one character</li>
* <li>{@code *} matches zero or more characters</li>
* <li>{@code **} matches zero or more <em>directories</em> in a path</li>
* <li>{@code {spring:[a-z]+}} matches the regexp {@code [a-z]+} as a path variable named "spring"</li>
* </ul>
*
* <h3>Examples</h3>
* <ul>
* <li>{@code com/t?st.jsp} — matches {@code com/test.jsp} but also
* {@code com/tast.jsp} or {@code com/txst.jsp}</li>
* <li>{@code com/*.jsp} — matches all {@code .jsp} files in the
* {@code com} directory</li>
* <li><code>com/**/test.jsp</code> — matches all {@code test.jsp}
* files underneath the {@code com} path</li>
* <li><code>org/springframework/**/*.jsp</code> — matches all
* {@code .jsp} files underneath the {@code org/springframework} path</li>
* <li><code>org/**/servlet/bla.jsp</code> — matches
* {@code org/springframework/servlet/bla.jsp} but also
* {@code org/springframework/testing/servlet/bla.jsp} and {@code org/servlet/bla.jsp}</li>
* <li>{@code com/{filename:\\w+}.jsp} will match {@code com/test.jsp} and assign the value {@code test}
* to the {@code filename} variable</li>
* </ul>
*
* <p><strong>Note:</strong> a pattern and a path must both be absolute or must
* both be relative in order for the two to match. Therefore it is recommended
* that users of this implementation to sanitize patterns in order to prefix
* them with "/" as it makes sense in the context in which they're used.
* /

符号的规则定义标准

  • ? 匹配1个字符
  • * 匹配0个或多个字符
  • ** 匹配路径中的0个或多个目录
  • {spring:[a-z]+} 将正则表达式[a-z]+匹配到的值,赋值给名为spring的路径变量。

必须是完全匹配才行,在SpringMVC中只有完全匹配才会进入controller层的方法

和其它几个不一样的是? 要求必须为一个字符,并且不能是代表路径分隔符的/。

@RequestMapping("/index?")
@ResponseBody
public String index(){
return "index.html";
}
index false 404错误(必须要有一个字符)
index/ false 404错误(不能为"/")
indexab false 404错误(不能是多个字符)
indexa true 输出页面index.html
`
  • ,虽然可以匹配多个任意的字符,但是,无法用 * 可以替代 ** ,因为 * 代表的多个任意字符组成的字符串不能是个目录或者说路径.也就是说,* 并不能拿来替代 **.
示例代码:
@RequestMapping("/index*")
@ResponseBody
public String index(){
return "index.html";
}
index true 输出index.html(可以为0字符)
index/ true 输出 index.html(可以为"/")
indexa true 输出 index.html(可以为1个字符)
indexabc true 输出 index.html(可以为多个字符)
index/a false 404错误("/a"是一个路径)
`

0个或多个目录.** 代表的字符串本身不一定要包含 /

@RequestMapping("/index/**/a")
@ResponseBody
public String index(){
return "index.html";
}
index/a true 输出 index.html(可以为0个目录)
index/x/a true 输出 index.html(可以为一个目录)
index/x/z/c/a true 输出 index.html(可以为多个目录)
`

符号 {spring:[a-z]+}

其它的关于 AntPathMatcher 的文章里,对 {spring:[a-z]+} 的匹配大多是只字未提.这里补充下.示例代码:

@RequestMapping("/index/{username:[a-b]+}") @ResponseBody public String index(@PathVariable("username") String username){ System.out.println(username); return username; }

结果:

index/ab true 输出 ab index/abbaaa true 输出 abbaaa index/a false 404错误 index/ac false 404错误

需求:我在做rbac权限校验的时候,设置管理员的访问路径为/admin/**,希望所有的开头为/admin/的uri操作地址都能进行匹配判断。

手动使用方式
  • AntPathMatcher不仅可以匹配Spring的@RequestMapping路径,也可以用来匹配各种字符串,包括文件路径等。
  • AntPathMatcher默认路径分隔符为“/”,而在匹配文件路径时,需要注意Windows下路径分隔符为“\”,Linux下为“/”,写法即为:
初始化创建操作

匹配文件路径,使用AntPathMatcher创建一个对象时,需要注意AntPathMatcher也有有参构造,传递路径分隔符参数pathSeparator,对于文件路径的匹配来说,则需要根据不同的操作系统来传递各自的文件分隔符,以此防止匹配文件路径错误。

AntPathMatcher matcher = new AntPathMatcher(File.separator);
AntPathMatcher matcher = new AntPathMatcher(System.getProperty("file.separator"));
执行匹配操作
import org.springframework.util.AntPathMatcher;
String content = "/admin/acuff";
String pattern = "/admin/**";
System.out.println(antPathMatcher.match(pattern, content));
最长匹配原则(has more characters)

最长匹配规则(has more characters),即越精确的模式越会被优先匹配到。例如,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式/**/*.jsp和/app/dir/*.jsp,那么会根据模式/app/dir/*.jsp来匹配。当然如果觉得这个工具还不够强大,还可以使用RegexRequestMatcher ,它支持使用正则表达式对URL地址进行匹配。如果你觉得这些都不够强大可以自己重写 RequestMatcher接口来进行定制的路由匹配规则

摘取网上的案例参考Sample

// test exact matching
assertTrue(pathMatcher.match("test", "test"));
assertTrue(pathMatcher.match("/test", "/test"));
assertTrue(pathMatcher.match("http://example.org", "http://example.org")); // SPR-14141
assertFalse(pathMatcher.match("/test.jpg", "test.jpg"));
assertFalse(pathMatcher.match("test", "/test"));
assertFalse(pathMatcher.match("/test", "test"));

// test matching with ?'s
assertTrue(pathMatcher.match("t?st", "test"));
assertTrue(pathMatcher.match("??st", "test"));
assertTrue(pathMatcher.match("tes?", "test"));
assertTrue(pathMatcher.match("te??", "test"));
assertTrue(pathMatcher.match("?es?", "test"));
assertFalse(pathMatcher.match("tes?", "tes"));
assertFalse(pathMatcher.match("tes?", "testt"));
assertFalse(pathMatcher.match("tes?", "tsst"));

// test matching with *'s
assertTrue(pathMatcher.match("*", "test"));
assertTrue(pathMatcher.match("test*", "test"));
assertTrue(pathMatcher.match("test*", "testTest"));
assertTrue(pathMatcher.match("test/*", "test/Test"));
assertTrue(pathMatcher.match("test/*", "test/t"));
assertTrue(pathMatcher.match("test/*", "test/"));
assertTrue(pathMatcher.match("*test*", "AnothertestTest"));
assertTrue(pathMatcher.match("*test", "Anothertest"));
assertTrue(pathMatcher.match("*.*", "test."));
assertTrue(pathMatcher.match("*.*", "test.test"));
assertTrue(pathMatcher.match("*.*", "test.test.test"));
assertTrue(pathMatcher.match("test*aaa", "testblaaaa"));
assertFalse(pathMatcher.match("test*", "tst"));
assertFalse(pathMatcher.match("test*", "tsttest"));
assertFalse(pathMatcher.match("test*", "test/"));
assertFalse(pathMatcher.match("test*", "test/t"));
assertFalse(pathMatcher.match("test/*", "test"));
assertFalse(pathMatcher.match("*test*", "tsttst"));
assertFalse(pathMatcher.match("*test", "tsttst"));
assertFalse(pathMatcher.match("*.*", "tsttst"));
assertFalse(pathMatcher.match("test*aaa", "test"));
assertFalse(pathMatcher.match("test*aaa", "testblaaab"));

// test matching with ?'s and /'s
assertTrue(pathMatcher.match("/?", "/a"));
assertTrue(pathMatcher.match("/?/a", "/a/a"));
assertTrue(pathMatcher.match("/a/?", "/a/b"));
assertTrue(pathMatcher.match("/??/a", "/aa/a"));
assertTrue(pathMatcher.match("/a/??", "/a/bb"));
assertTrue(pathMatcher.match("/?", "/a"));

// test matching with <strong>'s
assertTrue(pathMatcher.match("/</strong>", "/testing/testing"));
assertTrue(pathMatcher.match("/*/<strong>", "/testing/testing"));
assertTrue(pathMatcher.match("/</strong>/*", "/testing/testing"));
assertTrue(pathMatcher.match("/bla/<strong>/bla", "/bla/testing/testing/bla"));
assertTrue(pathMatcher.match("/bla/</strong>/bla", "/bla/testing/testing/bla/bla"));
assertTrue(pathMatcher.match("/<strong>/test", "/bla/bla/test"));
assertTrue(pathMatcher.match("/bla/</strong>/**/bla", "/bla/bla/bla/bla/bla/bla"));
assertTrue(pathMatcher.match("/bla*bla/test", "/blaXXXbla/test"));
assertTrue(pathMatcher.match("/*bla/test", "/XXXbla/test"));
assertFalse(pathMatcher.match("/bla*bla/test", "/blaXXXbl/test"));
assertFalse(pathMatcher.match("/*bla/test", "XXXblab/test"));
assertFalse(pathMatcher.match("/*bla/test", "XXXbl/test"));

assertFalse(pathMatcher.match("/????", "/bala/bla"));
assertFalse(pathMatcher.match("/**/*bla", "/bla/bla/bla/bbb"));

assertTrue(pathMatcher.match("/*bla*/<strong>/bla/</strong>", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
assertTrue(pathMatcher.match("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
assertTrue(pathMatcher.match("/*bla*/<strong>/bla/</strong>", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
assertTrue(pathMatcher.match("/*bla*/<strong>/bla/</strong>", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));

assertTrue(pathMatcher.match("*bla*/<strong>/bla/</strong>", "XXXblaXXXX/testing/testing/bla/testing/testing/"));
assertTrue(pathMatcher.match("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing"));
assertTrue(pathMatcher.match("*bla*/<strong>/bla/</strong>", "XXXblaXXXX/testing/testing/bla/testing/testing"));
assertFalse(pathMatcher.match("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing"));

assertFalse(pathMatcher.match("/x/x/<strong>/bla", "/x/x/x/"));

assertTrue(pathMatcher.match("/foo/bar/</strong>", "/foo/bar")) ;

assertTrue(pathMatcher.match("", ""));

assertTrue(pathMatcher.match("/{bla}.*", "/testing.html"));
`

spring mvc url地址匹配工具类

AntPathRequestMatcher

在spring mvc 中我们会经常使用//*.jsp、/app//dir/file.、/**/example 、/app/*.x 类似于这样语法而负责真正判断是否匹配的工具类就是AntPathRequestMatcher

  • 打赏
  • 1
  • 1收藏
  • 1评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK