[email protected]與RE
@RequestMapping 是 Spring Web 應用程式中最常被用到的註解之一。這個註解會將 HTTP 請求對映到 MVC 和 REST 控制器的處理方法上。
在這篇文章中,你將會看到 @RequestMapping 註解在被用來進行 Spring MVC 控制器方法的對映可以如何發揮其多才多藝的功能的。
Request Mapping 基礎用法
在 Spring MVC 應用程式中,RequestDispatcher (在 Front Controller 之下) 這個 servlet 負責將進入的 HTTP 請求路由到控制器的處理方法。
在對 Spring MVC 進行的配置的時候, 你需要指定請求與處理方法之間的對映關係。
要配置 Web 請求的對映,就需要你用上 @RequestMapping 註解。
@RequestMapping 註解可以在控制器類的級別和/或其中的方法的級別上使用。
在類的級別上的註解會將一個特定請求或者請求模式對映到一個控制器之上。之後你還可以另外新增方法級別的註解來進一步指定到處理方法的對映關係。
下面是一個同時在類和方法上應用了 @RequestMapping 註解的示例:
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping("/") String get() { //mapped to hostname:port/home/ return "Hello from get"; } @RequestMapping("/index") String index() { //mapped to hostname:port/home/index/ return "Hello from index"; } }
如上述程式碼所示,到 /home 的請求會由 get() 方法來處理,而到 /home/index 的請求會由 index() 來處理。
@RequestMapping 來處理多個 URI
你可以將多個請求對映到一個方法上去,只需要新增一個帶有請求路徑值列表的 @RequestMapping 註解就行了。
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping(value = { "", "/page", "page*", "view/*,**/msg" }) String indexMultipleMapping() { return "Hello from index multiple mapping."; } }
如你在這段程式碼中所看到的,@RequestMapping 支援統配符以及ANT風格的路徑。前面這段程式碼中,如下的這些 URL 都會由 indexMultipleMapping() 來處理:
- localhost:8080/home
- localhost:8080/home/
- localhost:8080/home/page
- localhost:8080/home/pageabc
- localhost:8080/home/view/
- localhost:8080/home/view/view
帶有 @RequestParam 的 @RequestMapping
@RequestParam 註解配合 @RequestMapping 一起使用,可以將請求的引數同處理方法的引數繫結在一起。
@RequestParam 註解使用的時候可以有一個值,也可以沒有值。這個值指定了需要被對映到處理方法引數的請求引數, 程式碼如下所示:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/id")
String getIdByValue(@RequestParam("id") String personId) {
System.out.println("ID is " + personId);
return "Get ID from query string of URL with value element";
}
@RequestMapping(value = "/personId")
String getId(@RequestParam String personId) {
System.out.println("ID is " + personId);
return "Get ID from query string of URL without value element";
}
}
在程式碼的第6行,id 這個請求引數被對映到了 thegetIdByValue() 這個處理方法的引數 personId 上。
如果請求引數和處理方法引數的名稱一樣的話,@RequestParam 註解的 value 這個引數就可省掉了, 如程式碼的第11行所示。
@RequestParam 註解的 required 這個引數定義了引數值是否是必須要傳的。
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/name")
String getName(@RequestParam(value = "person", required = false) String personName) {
return "Required element of request param";
}
}
在這段程式碼中,因為 required 被指定為 false,所以 getName() 處理方法對於如下兩個 URL 都會進行處理:
- /home/name?person=xyz
- /home/name
@RequestParam 的 defaultValue 取值就是用來給取值為空的請求引數提供一個預設值的。
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/name")
String getName(@RequestParam(value = "person", defaultValue = "John") String personName) {
return "Required element of request param";
}
}
在這段程式碼中,如果 person 這個請求引數為空,那麼 getName() 處理方法就會接收 John 這個預設值作為其引數。
用 @RequestMapping 處理 HTTP 的各種方法
Spring MVC 的 @RequestMapping 註解能夠處理 HTTP 請求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的請求預設都會是 HTTP GET 型別的。
為了能降一個請求對映到一個特定的 HTTP 方法,你需要在 @RequestMapping 中使用 method 來宣告 HTTP 請求所使用的方法型別,如下所示:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(method = RequestMethod.GET)
String get() {
return "Hello from get";
}
@RequestMapping(method = RequestMethod.DELETE)
String delete() {
return "Hello from delete";
}
@RequestMapping(method = RequestMethod.POST)
String post() {
return "Hello from post";
}
@RequestMapping(method = RequestMethod.PUT)
String put() {
return "Hello from put";
}
@RequestMapping(method = RequestMethod.PATCH)
String patch() {
return "Hello from patch";
}
}
在上述這段程式碼中, @RequestMapping 註解中的 method 元素聲明瞭 HTTP 請求的 HTTP 方法的型別。
所有的處理處理方法會處理從這同一個 URL( /home)進來的請求, 但要看指定的 HTTP 方法是什麼來決定用哪個方法來處理。
例如,一個 POST 型別的請求 /home 會交給 post() 方法來處理,而一個 DELETE 型別的請求 /home 則會由 delete() 方法來處理。
你會看到 Spring MVC 將使用這樣相同的邏輯來對映其它的方法。
用 @RequestMapping 來處理生產和消費物件
可以使用 @RequestMapping 註解的 produces 和 consumes 這兩個元素來縮小請求對映型別的範圍。
為了能用請求的媒體型別來產生物件, 你要用到 @RequestMapping 的 produces 元素再結合著 @ResponseBody 註解。
你也可以利用 @RequestMapping 的 comsumes 元素再結合著 @RequestBody 註解用請求的媒體型別來消費物件。
下面這段程式碼就用到的 @RequestMapping 的生產和消費物件元素:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/prod", produces = {
"application/JSON"
})
@ResponseBody
String getProduces() {
return "Produces attribute";
}
@RequestMapping(value = "/cons", consumes = {
"application/JSON",
"application/XML"
})
String getConsumes() {
return "Consumes attribute";
}
}
在這段程式碼中, getProduces() 處理方法會產生一個 JSON 響應, getConsumes() 處理方法可以同時處理請求中的 JSON 和 XML 內容。
使用 @RequestMapping 來處理訊息頭
@RequestMapping 註解提供了一個 header 元素來根據請求中的訊息頭內容縮小請求對映的範圍。
在可以指定 header 元素的值,用 myHeader = myValue 這樣的格式:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/head", headers = {
"content-type=text/plain"
})
String post() {
return "Mapping applied along with headers";
}
}
在上面這段程式碼中, @RequestMapping 註解的 headers 屬性將對映範圍縮小到了 post() 方法。有了這個,post() 方法就只會處理到 /home/head 並且 content-typeheader 被指定為 text/plain 這個值的請求。
你也可以像下面這樣指定多個訊息頭:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/head", headers = {
"content-type=text/plain",
"content-type=text/html"
}) String post() {
return "Mapping applied along with headers";
}
}
這樣, post() 方法就能同時接受 text/plain 還有 text/html 的請求了。
使用 @RequestMapping 來處理請求引數
@RequestMapping 直接的 params 元素可以進一步幫助我們縮小請求對映的定位範圍。使用 params 元素,你可以讓多個處理方法處理到同一個URL 的請求, 而這些請求的引數是不一樣的。
你可以用 myParams = myValue 這種格式來定義引數,也可以使用萬用字元來指定特定的引數值在請求中是不受支援的。
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/fetch", params = {
"personId=10"
})
String getParams(@RequestParam("personId") String id) {
return "Fetched parameter using params attribute = " + id;
}
@RequestMapping(value = "/fetch", params = {
"personId=20"
})
String getParamsDifferent(@RequestParam("personId") String id) {
return "Fetched parameter using params attribute = " + id;
}
}
在這段程式碼中,getParams() 和 getParamsDifferent() 兩個方法都能處理相同的一個 URL (/home/fetch) ,但是會根據 params 元素的配置不同而決定具體來執行哪一個方法。
例如,當 URL 是 /home/fetch?id=10 的時候, getParams() 會執行,因為 id 的值是10,。對於 localhost:8080/home/fetch?personId=20 這個URL, getParamsDifferent() 處理方法會得到執行,因為 id 值是 20。
使用 @RequestMapping 處理動態 URI
@RequestMapping 註解可以同 @PathVaraible 註解一起使用,用來處理動態的 URI,URI 的值可以作為控制器中處理方法的引數。你也可以使用正則表示式來只處理可以匹配到正則表示式的動態 URI。
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/fetch/{id}", method = RequestMethod.GET)
String getDynamicUriValue(@PathVariable String id) {
System.out.println("ID is " + id);
return "Dynamic URI parameter fetched";
}
@RequestMapping(value = "/fetch/{id:[a-z]+}/{name}", method = RequestMethod.GET)
String getDynamicUriValueRegex(@PathVariable("name") String name) {
System.out.println("Name is " + name);
return "Dynamic URI parameter fetched using regex";
}
}
在這段程式碼中,方法 getDynamicUriValue() 會在發起到 localhost:8080/home/fetch/10 的請求時執行。這裡 getDynamicUriValue() 方法 id 引數也會動態地被填充為 10 這個值。
方法 getDynamicUriValueRegex() 會在發起到 localhost:8080/home/fetch/category/shirt 的請求時執行。不過,如果發起的請求是 /home/fetch/10/shirt 的話,會丟擲異常,因為這個URI並不能匹配正則表示式。
@PathVariable 同 @RequestParam的執行方式不同。你使用 @PathVariable 是為了從 URI 裡取到查詢引數值。換言之,你使用 @RequestParam 是為了從 URI 模板中獲取引數值。
@RequestMapping 預設的處理方法
在控制器類中,你可以有一個預設的處理方法,它可以在有一個向預設 URI 發起的請求時被執行。
下面是預設處理方法的示例:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping()
String
default () {
return "This is a default method for the class";
}
}
在這段程式碼中,向 /home 發起的一個請求將會由 default() 來處理,因為註解並沒有指定任何值。
@RequestMapping 快捷方式
Spring 4.3 引入了方法級註解的變體,也被叫做 @RequestMapping 的組合註解。組合註解可以更好的表達被註解方法的語義。它們所扮演的角色就是針對 @RequestMapping 的封裝,而且成了定義端點的標準方法。
例如,@GetMapping 是一個組合註解,它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一個快捷方式。
方法級別的註解變體有如下幾個:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
如下程式碼展示瞭如何使用組合註解:
@RestController
@RequestMapping("/home")
public class IndexController {
@GetMapping("/person")
public @ResponseBody ResponseEntity < String > getPerson() {
return new ResponseEntity < String > ("Response from GET", HttpStatus.OK);
}
@GetMapping("/person/{id}")
public @ResponseBody ResponseEntity < String > getPersonById(@PathVariable String id) {
return new ResponseEntity < String > ("Response from GET with id " + id, HttpStatus.OK);
}
@PostMapping("/person")
public @ResponseBody ResponseEntity < String > postPerson() {
return new ResponseEntity < String > ("Response from POST method", HttpStatus.OK);
}
@PutMapping("/person")
public @ResponseBody ResponseEntity < String > putPerson() {
return new ResponseEntity < String > ("Response from PUT method", HttpStatus.OK);
}
@DeleteMapping("/person")
public @ResponseBody ResponseEntity < String > deletePerson() {
return new ResponseEntity < String > ("Response from DELETE method", HttpStatus.OK);
}
@PatchMapping("/person")
public @ResponseBody ResponseEntity < String > patchPerson() {
return new ResponseEntity < String > ("Response from PATCH method", HttpStatus.OK);
}
}
在這段程式碼中,每一個處理方法都使用 @RequestMapping 的組合變體進行了註解。儘管每個變體都可以使用帶有方法屬性的 @RequestMapping 註解來互換實現, 但組合變體仍然是一種最佳的實踐 — 這主要是因為組合註解減少了在應用程式上要配置的元資料,並且程式碼也更易讀。
@RequestMapping 總結
如你在本文中所看到的,@RequestMapping 註解是非常靈活的。你可以使用該註解配置 Spring MVC 來處理大量的場景用例。它可以被用來在 Spring MVC 中配置傳統的網頁請求,也可以是 REST 風格的 Web 服務。
chenyb 隨筆記錄,方便自己學習 (本文借鑑Using the Spring @RequestMapping Annotation)
2018-11-1