您好,登錄后才能下訂單哦!
WebFlux中的Path參數解析與url映射是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
WebFlux 之 Path 參數解析與 url 映射
異步、反應式、函數式編程,近來可以說是逐漸主流了;Spring5 通過 Reactor 增加了對反應式編程的支持,而 Spring WebFlux 不同于以往的 web 框架,作為一個非阻塞異步 web 框架,可以充分的利用多核 CPU 硬件資源,提供更強的并發支持;Spring 官方對 WebFlux 的支持非常友好,基本上對于慣于 Spring WEB 的 java 開發者,可以很簡單的遷移過來
接下來我們將進入 WebFlux 系列教程,努力使用最簡明的語言,來介紹一下 WebFlux 的基本玩法,讓各位小伙伴可以順暢的切換和使用 WebFlux 來體驗反應式編程的魅力
小編將主要介紹 WebFlux 提供 web 接口時的 url 匹配,以及對應的 path 參數解析
本項目借助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
進行開發
使用 WebFlux,最主要的引入依賴如下(省略掉了 SpringBoot 的相關依賴,如對于如何創建 SpringBoot 項目不太清楚的小伙伴,可以關注一下我之前的博文)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>
下面所有內容基于官方文檔完成: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-ann-requestmapping-uri-templates
下面的示例主要是基于注解的方式,基本知識點和 SpringWeb 沒有太大的區別(至于函數式的用法,后面會專門介紹)
path 參數,舉例如: http://127.0.0.1:8080/name/test
中name
和test
就算是 path 參數,我們主要是借助@PathVariable
來獲取
一個具體實例
@RestController @RequestMapping(path = "path") public class PathAction { /** * 最基本的path獲取方式 * * @param index * @return */ @GetMapping(path = "/basic/{index}") public Mono<String> basic(@PathVariable(name = "index") int index) { return Mono.just("path index: " + index); } }
針對上面的 case,我們簡單的設計了三個訪問 case,具體結果如下
? ~ curl 'http://127.0.0.1:8080/path/basic/1' path index: 1% ? ~ curl 'http://127.0.0.1:8080/path/basic/1/2' {"timestamp":"2020-08-26T13:35:26.221+0000","path":"/path/basic/1/2","status":404,"error":"Not Found","message":null,"requestId":"8256bf73"}% ? ~ curl 'http://127.0.0.1:8080/path/basic/' {"timestamp":"2020-08-26T13:35:32.196+0000","path":"/path/basic/","status":404,"error":"Not Found","message":null,"requestId":"eeda1111"}%
請注意上面的輸出,/basic/{index}
只能匹配單級的 path 路徑參數,而且上面的寫法中,這級 path 路徑必須存在
查看PathVariable
注解可以看到里面有一個required
屬性,如果設置為 false,會怎樣呢
@GetMapping(path = "/basic2/{index}") public Mono<String> basic2(@PathVariable(name = "index", required = false) Integer index) { return Mono.just("basic2 index: " + index); }
測試 case 如下
? ~ curl 'http://127.0.0.1:8080/path/basic2/' {"timestamp":"2020-08-26T13:41:40.100+0000","path":"/path/basic2/","status":404,"error":"Not Found","message":null,"requestId":"b2729e2c"}% ? ~ curl 'http://127.0.0.1:8080/path/basic2/22' basic2 index: 22% ? ~ curl 'http://127.0.0.1:8080/path/basic2/22/3' {"timestamp":"2020-08-26T13:41:44.400+0000","path":"/path/basic2/22/3","status":404,"error":"Not Found","message":null,"requestId":"0b3f173c"}%
從上面的實際 case,也可以看出來,級別這個屬性設置為 false,但是 url 路徑依然需要正確匹配,多一級和少一級都不行
上面只有一個 path 參數,如果有多個參數,也比較簡單
/** * 多個參數的場景 * * @param index * @param order * @return */ @GetMapping(path = "/mbasic/{index}/{order}") public Mono<String> mbasic(@PathVariable(name = "index") int index, @PathVariable(name = "order") String order) { return Mono.just("mpath arguments: " + index + " | " + order); }
測試 case 如下
? ~ curl 'http://127.0.0.1:8080/path/mbasic/1/asc' mpath arguments: 1 | asc%
上面的兩個 case,都是完整的匹配某一級路徑,下面介紹部分匹配的 case
/** * 路徑中的部分內容匹配 * * - /part/test.txt -> name = test * - /part/a/test.txt -> 不匹配 * * @param name * @return */ @GetMapping(path = "/part/{name}.txt") public Mono<String> part(@PathVariable(name = "name") String name) { return Mono.just("part path argument: " + name); }
請注意上面的 path 路徑,后綴是.txt
,如下面的實例中part/hello.txt
中那么對應的就是hello
? ~ curl 'http://127.0.0.1:8080/path/part/hello.txt' part path argument: hello% ? ~ curl 'http://127.0.0.1:8080/path/part/hello.tx' {"timestamp":"2020-08-26T13:47:49.121+0000","path":"/path/part/hello.tx","status":404,"error":"Not Found","message":null,"requestId":"1075d683"}%
接下來更高端的 path 參數匹配來了,支持一些簡單的正則,如我們希望對spring-web-3.0.5.jar
這段 path 路徑進行解析,希望將spring-web
作為name
, 3.0.5
作為version
,.jar
作為ext
因此我們的 rest 接口寫法可以如下
/** * 正則匹配 * * /path/path/pattern/spring-web-3.0.5.jar -> name = spring-web, version=3.0.5, ext=.jar * * @return */ @GetMapping(path = "/pattern/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") public Mono<String> urlPattern(@PathVariable(name = "name") String name, @PathVariable(name = "version") String version, @PathVariable(name = "ext") String ext) { return Mono.just("pattern arguments name=" + name + " version=" + version + " ext=" + ext); }
注意上面的所有寫法,都有一個特點,那就是只能針對單級的 path 路徑進行全/部分匹配(本文中將 path 路徑中//
之間作為一級),那么如果我希望我的 path 參數可以匹配多級,可以怎么辦
如 /path/name/hello
請求路徑中,我希望將 /name/hello
作為一個 path 參數
針對上面的場景,我們主要是借助{*name}
方式來處理,注意這個參數名前面的*號
/** * 匹配: * * - /path/pattern2 -> name == "" * - /path/pattern2/hello -> name == /hello * - /path/pattern2/test/hello -> name = /test/hello * * @param name * @return */ @GetMapping(path = "/pattern2/{*name}") public Mono<String> pattern2(@PathVariable(name = "name") String name) { return Mono.just("pattern2 argument: " + name); }
測試 case 如下
? ~ curl 'http://127.0.0.1:8080/path/pattern2' pattern2 argument: % ? ~ curl 'http://127.0.0.1:8080/path/pattern2/hello' pattern2 argument: /hello% ? ~ curl 'http://127.0.0.1:8080/path/pattern2/hello/world' pattern2 argument: /hello/world%
前面介紹的是 path 參數解析,接下來我們簡單的看一下最常見的三種路徑匹配方式
一個星號,表示匹配 0 個 or1 個單級 path 路徑
/** * 單個*號,只能匹配一級目錄,注意這種方式與上面的 pattern2 之間的區別 * * 可以匹配: * * - /path/pattern3/hello * - /path/pattern3 * * 不能匹配 * * - /path/pattern3/hello/1 * * @return */ @GetMapping(path = "/pattern3/*") public Mono<String> pattern3() { return Mono.just("pattern3 succeed!"); }
實測 case 如下
# 請注意,這里是沒有/結尾的 ? ~ curl 'http://127.0.0.1:8080/path/pattern3' {"timestamp":"2020-08-27T00:01:20.703+0000","path":"/path/pattern3","status":404,"error":"Not Found","message":null,"requestId":"c88f5066"}% ? ~ curl 'http://127.0.0.1:8080/path/pattern3/' pattern3 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern3/a' pattern3 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern3/a/b' {"timestamp":"2020-08-27T00:01:18.144+0000","path":"/path/pattern3/a/b","status":404,"error":"Not Found","message":null,"requestId":"203dc7d4"}%
請注意上面的實例,/path/pattern3
訪問 404, 而/path/pattern3/
是可以的,唯一的區別就是多了一個后綴/
why?
是因為 path 路徑的星號前面有一個/
導致的么?
接下來我們再設計一個 case,將*
前面的/
干掉,再測試一下
@GetMapping(path = "/pattern33**") public Mono<String> pattern33() { return Mono.just("pattern33 succeed!"); }
再次測試,結果如下
? ~ curl 'http://127.0.0.1:8080/path/pattern3311' pattern33 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern33/11' {"timestamp":"2020-08-27T00:05:51.236+0000","path":"/path/pattern33/11","status":404,"error":"Not Found","message":null,"requestId":"d8cbd546"}% ? ~ curl 'http://127.0.0.1:8080/path/pattern33' pattern33 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern331/' pattern33 succeed!%
借助前面兩個 case,我們基本上可以看出*
的作用
*
前面的完全匹配
比如/pattern3/*
,那么訪問的 path 路徑前綴必須是/pattern3/
*
最多表示單級路徑,簡單來講就是*
所代表的的位置中不能出現/x
比如/pattern33**
,那么/pattern331/
可以匹配,但是/pattern331/1
不能
有別與上面的單個*
匹配 0-1 級 path 路徑,兩個**
則表示可以一直匹配到最后一層
/** * 對于 pattern4開頭的都可以匹配 * * @return */ @GetMapping(path = "/pattern4/**") public Mono<String> pattern4() { return Mono.just("pattern4 succeed!"); }
測試 case 如下
? ~ curl 'http://127.0.0.1:8080/path/pattern4' pattern4 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern4/12' pattern4 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern4/12/3' pattern4 succeed!%
請注意
直接訪問/pattern4
也是可以命中的,這個和上面是有區別的
單個字符的通配,比較簡單如下
/** * 匹配 pattern5/test pattern5/tast ... * 不匹配 pattern5/tst pattern5/tesst * * @return */ @GetMapping(path = "/pattern5/t?st") public Mono<String> pattern5() { return Mono.just("pattern5 succeed!"); }
訪問 case
? ~ curl 'http://127.0.0.1:8080/path/pattern5/test' pattern5 succeed!% ? ~ curl 'http://127.0.0.1:8080/path/pattern5/t/st' {"timestamp":"2020-08-27T00:13:42.557+0000","path":"/path/pattern5/t/st","status":404,"error":"Not Found","message":null,"requestId":"add34639"}% ? ~ curl 'http://127.0.0.1:8080/path/pattern5/tst' {"timestamp":"2020-08-27T00:14:01.078+0000","path":"/path/pattern5/tst","status":404,"error":"Not Found","message":null,"requestId":"b2691121"}%
從上面的測試輸出也可以看出
?
對應的地方不能是/
以及其他不被支持的字符(如?
,'
,"
, %
等)
?
對應的地方必須存在
雖然本文的主題是 webflux 中 path 參數解析與 url 映射匹配,但是看下來我們會神奇的發現,這些知識點和 SpringMVC 中,貌似也沒有什么區別,事實上也確實如此;對于注解的使用場景時,絕大多數,都是之前怎么玩,現在依然可以怎么玩
下面用一個表格針對上面的知識點進行匯總
pattern | 描述 | 舉例 |
---|---|---|
? | 匹配一個字符 | pages/t?st.html 匹配 /pages/test.html and /pages/t3st.html |
* | 匹配單級 path 路徑中 0-多個字符 | "/resources/*.png" matches "/resources/file.png" <br/> "/projects/*/versions" matches "/projects/spring/versions" but does not match "/projects/spring/boot/versions" |
** | 匹配 0-多個 path 路徑 | "/resources/**" matches "/resources/file.png" and "/resources/images/file.png" <br/> 而"/resources/**/file.png" 這種寫法是非法的 |
{name} | 匹配單級 path 路徑參數 | "/projects/{project}/versions" matches "/projects/spring/versions" and captures project=spring |
{name:[a-z]+} | 正則 | "/projects/{project:[a-z]+}/versions" matches "/projects/spring/versions" but not "/projects/spring1/versions" |
{*path} | 匹配 path 路徑中,0-最后一級 path 路徑參數 | "/resources/{*file}" matches "/resources/images/file.png" and captures file=images/file.png |
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。