springMVC4(2)請求對映全面分析
在springMVC的控制器中,我們常使用@RequestMapping來完成我們的請求對映,我們可以在類定義上和方法定義上使用註解,其配置的路徑將為類中定義的所有方法的父路徑,如上篇例項中的/user(類)/hello(方法)。
一般的,我們類定義上的路徑註解起到名稱空間的作用,防止不同方法的路徑對映產生衝突,比如我在UserController和ArticleController下都定義瞭如下的方法:
@RequestMapping("list")
public void list(){
....
}
一個list對映路徑,這時候springMVC就不知道該將請求交給到哪個方法處理。當然,我們也能在方法上進行二級路徑配置區分:
/*************UserController***********/
@RequestMapping("user/list")
public void list(){
....
}
/*************ArticleController***********/
@RequestMapping("article/list")
public void list(){
....
}
這樣就能有效防止衝突了,但如果我有很多個方法存在這樣的衝突,是否都要在每個方法加上字首呢?這時候我們可以選擇在類路徑上註解@RequestMapping來對全體方法進行區分。
通過url進行對映
1. Ant風格字元匹配
除了標準的url外,@RequestMapping還支援Ant風格字元,即”?”、”*”、”**”,其中
1. “?”:匹配一個任意字元,如/user/a?,匹配user/aa,user/ab等路徑
2. “*”:匹配任意字串,如/user/a*,匹配/user下任意以a開頭的路徑如/user/abc,/user/aqw等
3. “**“:匹配多級路徑字串,如/user/**/list,匹配/user/user1/list,/user/1resu/list等
在這裡,需要注意的是當*的位置與個數不同時,*可以代表的字元數有區別,看下面示例:
@RequestMapping("u1/*" )//只能匹配u1/a,u1/b,不能匹配u1/————即此時*表示一個或多個字元
public void test(HttpServletResponse response) throws IOException{
response.getWriter().print("u1/*");
}
@RequestMapping("u1/**")//能夠匹配u1/,u1/qq,u1/qq/ww,這裡要特別注意的是,“**“能匹配零個而“*”不能
public void test(HttpServletResponse response) throws IOException{
response.getWriter().print("u1/*");
}
@RequestMapping("u2/a*")//能夠匹配u2/a,u2/ab,u2/aqqqq等————即此時*表示零個或零個以上字元
public void test1(HttpServletResponse response) throws IOException{
response.getWriter().print("u2/a*");
}
2. restful佔位符匹配
除了使用上面風格,@RequestMapping還支援restful風格佔位符的形式,假如我們需要針對特定使用者檢視其特定文章,restful風格路徑匹配如下所示:
@Controller//註解為控制器,通過spring容器掃描,會註冊為一個Bean
@RequestMapping("/user/{uid}")//一級訪問路徑,對類中所有方法生效
public class UserController {
@RequestMapping("article/{aid}")
public String detail(@PathVariable("uid")Integer uid,@PathVariable("aid")Integer aid){
System.out.println( "檢視id為" + uid + "的使用者文章,且文章id為"+aid);
return "someplace";
}
}
這裡,如果我們想訪問使用者id為1,文章id為2的使用者文章,就可以訪問如下路徑:[專案根路徑]/user/1/article/2
來完成。
我們使用@PathVariable(“val”)來完成對應路徑中{val}的資源請求,這裡的兩個val名稱需一致,緊接著的方法入參名字任意,我們剛剛示例了一個多路徑引數繫結,假設只有一個,如下也是合法的:
@RequestMapping("user/{uid}")
public String detail(@PathVariable("uid")Integer notUid){//notUid名字也能成功繫結
return "someplace";
}
此外,如果我們入參名字和url路徑資源名稱一致,則可以省略配置@PathVariable中的value值,如下例項也能正確繫結路徑資源到入參
@RequestMapping("user/{uid}")
public String detail(@PathVariable Integer uid){//notUid名字也能成功繫結
return "someplace";
}
3. 優先匹配規則
url還有如下兩個常見匹配準則:最長最精確優先匹配和佔位符優先匹配
1. 最長最精確優先匹配
下面我們來看一個匹配例項:
@RequestMapping("test/**")
public void test2(HttpServletResponse response) throws IOException{
response.getWriter().print("test/**");
}
@RequestMapping("test/*")
public void test3(HttpServletResponse response) throws IOException{
response.getWriter().print("test/*");
}
@RequestMapping("test/*/**")
public void test4(HttpServletResponse response) throws IOException{
response.getWriter().print("test/*/**");
}
@RequestMapping("test/*/*")
public void test5(HttpServletResponse response) throws IOException{
response.getWriter().print("test/*/*");
}
@RequestMapping("test/1/*")
public void test6(HttpServletResponse response) throws IOException{
response.getWriter().print("test/1/*");
}
@RequestMapping("test/1/2")
public void test7(HttpServletResponse response) throws IOException{
response.getWriter().print("test/1/2");
}
直接看上面匹配會覺得很亂,我們直接看下面的測試:
測試 | 匹配結果 |
---|---|
test/a | 匹配test/*而不匹配test/**(更精確優先匹配) |
test/a/a/aa/a | 匹配test/**而不匹配test/*/**,(在多層匹配中,**比*/**更精確) |
test/a/a | 匹配test/*/*,因為/*/*比**精確 |
test/1/a | 匹配test/1/*,因為/1/*比/*/*精確 |
test/1/2 | 匹配test/1/2,這是完全匹配 |
2. 佔位符優先匹配原則
佔位符是指@PathVariable等路徑資源佔位符,下面我們在看一個例項
@RequestMapping("test/1/2")
public void test7(HttpServletResponse response) throws IOException{
response.getWriter().print("test/1/2");
}
@RequestMapping("test/1/{id}")
public void test8(HttpServletResponse response,@PathVariable Integer id ) throws IOException{
response.getWriter().print("test/1/(myId=)" + id );
}
@RequestMapping("test/1/a")
public void test7(HttpServletResponse response) throws IOException{
response.getWriter().print("test/1/a");
}
從上一個例項的所有路徑對映中,我們測試出test/1/2是最精確的。但我們根據添加了佔位符對映,在遊覽器輸入test/1/2,此時遊覽器返回test/1/(myId=)2,即佔位符的優先順序比普通字串的優先順序更高!但如果我們此時輸入test/1/a。程式不會因為我們的在方法入參中id對映為Integer型別而放棄匹配,佔位符的優先順序依然比字元(串)a的優先順序高,但由於“a”不能轉化為Integer型別,所以伺服器會返回400錯誤
通過HTTP其它請求資源對映
除了使用url外,我們還能通過請求引數、請求方法、或請求頭進行對映
我們先看看@RequestMapping的完整屬性列表:
屬性 | 說明 |
---|---|
value | 指定請求的實際地址, 比如 /action/info之類。 |
method | 指定請求的method型別, GET、POST、PUT、DELETE等 |
consumes | 指定處理請求的提交內容型別(Content-Type),例如application/json, text/html; |
produces | 指定返回的內容型別,僅當request請求頭中的(Accept)型別中包含該指定型別才返回 |
params | 指定request中必須包含某些引數值是,才讓該方法處理 |
headers | 指定request中必須包含某些指定的header值,才能讓該方法處理請求 |
其中,consumes, produces使用content-type資訊進行過濾資訊;headers中可以使用content-type進行過濾和判斷。
在前面的使用中,我們發現並沒有指定value屬性,直接在括號裡輸入字串也能向value屬性賦值,這是因為在java註解中不加其他屬性,直接賦值必定是針對註解的value成員,如果該註解沒有名為value的成員,則會報錯
下面我們先看幾個示例:
示例1:vmethod,headers
@RequestMapping(value = "testa",method = RequestMethod.POST,headers = "content-type=text/*")
表示對映路徑為testa,請求方法必須為POST方法(如果我們用post發出請求,會返回錯誤資訊HTTP Status 405 - Request method ‘GET’ not supported),headers部分表示請求頭資訊中必須包含等號後相應部分內容,*匹配任意字串
示例2:consumes
@RequestMapping(value = "testb", consumes="application/json")
表示方法僅匹配request Content-Type為“application/json”型別的請求。
示例3:produces
@RequestMapping(value = "/testc", produces="application/json")
表示方法匹配的請求需要請求頭中Accept部分包含”application/json“,同時在響應時,會將返回內容同時設定為”application/json‘’
示例4:params
@RequestMapping(value = "testd",method = RequestMethod.GET,params = {"id1","id2"})
public void test12(HttpServletResponse response,Integer id1,Integer id2) throws IOException{
response.getWriter().print(id1 + "——" + id2);
}
示例表示入參需包含引數名為id1,id2的兩個引數,這裡如果我輸入:
1. http://localhost:8080/springMVC/user/testd
—-
2. http://localhost:8080/springMVC/user/testd?id1=1
—報404錯誤
3. ttp://localhost:8080/springMVC/user/testd?id1=1&id2=2
—-返回1——2
4. ttp://localhost:8080/springMVC/user/testd?id1=1&id2=2&id3=3
—-返回1——2
從以上我們可以看出,只有具有相應引數的才能完成對映,且可以有除了params中要求以外的引數,如id3。
在params的常見對映規則如下:
示例規則 | 說明 |
---|---|
”param1” | 請求必須包含名為param1的引數 |
“!param1” | 請求中不能包含名為param1的引數 |
“param1!=value1 | 請求中必須包含param1引數,但其值不能為value1 |
{“param1=value1”,”param2”} | 請求中需要包含param1引數和param2引數,且param1的值必須為value1 |