@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 服務。