1. 概述
在本教程中,我們將重點介紹 Spring MVC 中一個主要註解:@RequestMapping。
簡單來説,該註解用於將 Web 請求映射到 Spring Controller 方法。
2. <em/>RequestMapping 基礎
我們從一個簡單的例子開始:使用基本條件將 HTTP 請求映射到方法。 假設 Spring 默認在根上下文路徑 (“/”) 上提供內容。 本文中的所有 CURL 請求都依賴於默認的根上下文路徑。
2.1. RequestMapping — 按路徑定義
@RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath() {
return "Get some Foos";
}要測試此映射,可以使用簡單的 curl 命令:
curl -i http://localhost:8080/ex/foos2.2. @RequestMapping — HTTP 方法
HTTP 方法 參數沒有默認值。因此,如果我們不指定值,它將映射到任何 HTTP 請求。
以下是一個簡單的例子,類似於之前的例子,但這次映射到 HTTP POST 請求:
@RequestMapping(value = "/ex/foos", method = POST)
@ResponseBody
public String postFoos() {
return "Post some Foos";
}要通過 curl 命令測試 POST 請求:
curl -i -X POST http://localhost:8080/ex/foos3. RequestMapping 和 HTTP Headers
This section explains how Spring MVC uses HTTP headers to determine how to handle requests. HTTP headers are key to routing requests to the correct controller method.
Here's a breakdown of important headers:
- Content-Type: This header specifies the media type of the request body. Common values include
application/json,application/xml, andtext/plain. - Accept: This header indicates the types of content the client is willing to accept in the response.
- Cookie: Used to store session information and other client-side data.
- Authorization: Used to authenticate the client.
3.1. 使用 @RequestMapping 屬性指定請求頭
映射可以進一步縮小範圍,通過指定請求頭來精確匹配:
@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET)
@ResponseBody
public String getFoosWithHeader() {
return "Get some Foos with Header";
}為了測試其操作,我們將使用 curl 頭部支持:
curl -i -H "key:val" http://localhost:8080/ex/foos並且可以通過 headers 屬性使用多個標題,應用於 @RequestMapping:
@RequestMapping(
value = "/ex/foos",
headers = { "key1=val1", "key2=val2" }, method = GET)
@ResponseBody
public String getFoosWithHeaders() {
return "Get some Foos with Header";
}我們可以使用以下命令進行測試:
curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/ex/foos請注意,對於 curl 語法,冒號用於分隔 header 鍵和 header 值,與 HTTP 規範相同,而 Spring 中則使用等號。
3.2. <em @RequestMapping</em> 的 Consumes 和 Produces
對控制器方法產生的 媒體類型 進行映射值得特別關注。
可以通過上述引入的 <em @RequestMapping</em> 的 headers 屬性,根據請求的 Accept 頭部來映射請求。
@RequestMapping(
value = "/ex/foos",
method = GET,
headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
return "Get some Foos with Header Old";
}這種定義 Accept 頭的做法,匹配方式具有靈活性——它使用 contains 代替 equals,因此像下面這樣的請求仍然會正確映射:
curl -H "Accept:application/json,text/html"
http://localhost:8080/ex/foos從 Spring 3.1 開始,<strong ><em >@RequestMapping</em> <em ></strong> 註解現在具有 <em >produces</em> 和 <em >consumes</em> 屬性,專門用於此目的:
@RequestMapping(
value = "/ex/foos",
method = RequestMethod.GET,
produces = "application/json"
)
@ResponseBody
public String getFoosAsJsonFromREST() {
return "Get some Foos with Header New";
}此外,舊的通過 headers 屬性進行映射的方式,將在 Spring 3.1 版本及更高版本中自動轉換為新的 produces 機制,因此結果將完全相同。
同樣,通過 curl 消耗的方式也相同:
curl -H "Accept:application/json"
http://localhost:8080/ex/foos此外,產生 還支持多種值:
@RequestMapping(
value = "/ex/foos",
method = GET,
produces = { "application/json", "application/xml" }
)請注意,這些——舊的和新的指定 Accept 頭的映射方式,本質上是相同的,因此 Spring 不允許它們同時啓用。
同時啓用這兩種方法將會導致:
Caused by: java.lang.IllegalStateException: Ambiguous mapping found.
Cannot map 'fooController' bean method
java.lang.String
org.baeldung.spring.web.controller
.FooController.getFoosAsJsonFromREST()
to
{ [/ex/foos],
methods=[GET],params=[],headers=[],
consumes=[],produces=[application/json],custom=[]
}:
There is already 'fooController' bean method
java.lang.String
org.baeldung.spring.web.controller
.FooController.getFoosAsJsonFromBrowser()
mapped.最後,關於新的 產生 和 消耗 機制,需要特別説明,它們的行為與大多數其他註解不同:當以類型級別指定時,方法級別的註解不會補充,而是會覆蓋 類型級別的信息。
當然,如果您想深入瞭解如何使用 Spring 構建 REST API,請查看 新的 Spring REST 課程。
4. 使用 Path 變量的 RequestMapping</em/>
映射 URI 的一部分可以通過 @PathVariable 註解綁定到變量上。
4.1. 單個 @PathVariable
一個簡單的帶有單個路徑變量的示例:
@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable("id") long id) {
return "Get a specific Foo with id=" + id;
}這段內容可以測試如下:使用 curl:
curl http://localhost:8080/ex/foos/1如果方法參數的名稱與路徑變量名稱完全匹配,則可以通過使用不帶值的 @PathVariable 來簡化:
@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable String id) {
return "Get a specific Foo with id=" + id;
}請注意,@PathVariable 能夠自動進行類型轉換,因此我們也可以將 id 聲明為:
@PathVariable long id4.2. 多個 @PathVariable 參數
一個更復雜的 URI 可能需要將 URI 的多個部分映射到多個值:
@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariables
(@PathVariable long fooid, @PathVariable long barid) {
return "Get a specific Bar with id=" + barid +
" from a Foo with id=" + fooid;
}這可以很容易地使用 curl 命令進行測試,方法相同:
curl http://localhost:8080/ex/foos/1/bar/24.3. 使用正則表達式進行路徑變量映射
正則表達式也可以用於映射 @PathVariable。
例如,我們將映射限制為僅接受數值值作為 id:
@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
@PathVariable long numericId) {
return "Get a specific Bar with id=" + numericId;
}這將意味着以下 URI 將匹配:
http://localhost:8080/ex/bars/1但這不是:
http://localhost:8080/ex/bars/abc5. 使用 RequestParameters 的 RequestMapping
RequestMapping 允許您輕鬆地使用 @RequestParam 註解將 URL 參數映射到 URI。
我們現在正在將請求映射到 URI:
http://localhost:8080/ex/bars?id=100@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
@RequestParam("id") long id) {
return "Get a specific Bar with id=" + id;
}我們隨後使用 @RequestParam(“id”) 標註在控制器方法簽名中,提取 id 參數的值。
要發送帶有 id 參數的請求,我們將使用 curl 中的參數支持:
curl -i http://localhost:8080/ex/bars --get -d id=100在此示例中,參數直接綁定,並未先進行聲明。
對於更高級的場景,@RequestMapping 可以可選地定義參數,作為另一種縮小請求映射的方式:
@RequestMapping(value = "/ex/bars", params = "id", method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
@RequestParam("id") long id) {
return "Get a specific Bar with id=" + id;
}更靈活的映射關係也允許使用。可以設置多個 params 值,並且並非所有值都需要使用:
@RequestMapping(
value = "/ex/bars",
params = { "id", "second" },
method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParams(
@RequestParam("id") long id) {
return "Narrow Get a specific Bar with id=" + id;
}當然,例如向 URI 發出的請求如下:
http://localhost:8080/ex/bars?id=100&second=something將始終映射到最佳匹配項——即更窄的匹配項,它定義了 id 和 second 參數。
6. <emRequestMapping> 的邊緣情況
6.1. @RequestMapping — 多個路徑映射到同一個控制器方法
雖然單個 @RequestMapping 路徑值通常用於單個控制器方法(這只是一個好的實踐,而不是硬性規定),但在某些情況下,將多個請求映射到同一個方法可能是必要的。
在這種情況下, value 屬性的 @RequestMapping 能夠接受多個映射,而不是僅僅一個:
@RequestMapping(
value = { "/ex/advanced/bars", "/ex/advanced/foos" },
method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
return "Advanced - Get some Foos or Bars";
}現在這兩個 curl 命令應該命中相同的接口:
curl -i http://localhost:8080/ex/advanced/foos
curl -i http://localhost:8080/ex/advanced/bars6.2. @RequestMapping — 相同控制器方法處理多個 HTTP 請求方法
可以使用不同的 HTTP 謂詞(verbs)映射到同一個控制器方法:
@RequestMapping(
value = "/ex/foos/multiple",
method = { RequestMethod.PUT, RequestMethod.POST }
)
@ResponseBody
public String putAndPostFoos() {
return "Advanced - PUT and POST within single method";
}使用 curl,這兩個請求現在都會命中相同的 HTTP 方法:
curl -i -X POST http://localhost:8080/ex/foos/multiple
curl -i -X PUT http://localhost:8080/ex/foos/multiple6.3. @RequestMapping — 一個所有請求的備用方案
為了使用特定的 HTTP 方法(例如,對於 GET 請求)實現所有請求的簡單備用方案,請執行以下操作:
@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
return "Fallback for GET Requests";
}或者對於所有請求:
@RequestMapping(
value = "*",
method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
return "Fallback for All Requests";
}6.4. 歧義映射錯誤
當 Spring 評估兩個或多個請求映射為不同的控制器方法時,就會發生歧義映射錯誤。請求映射在具有相同的 HTTP 方法、URL、參數、標頭和媒體類型時才被認為是相同的。
例如,以下是一個歧義映射:
@GetMapping(value = "foos/duplicate" )
public String duplicate() {
return "Duplicate";
}
@GetMapping(value = "foos/duplicate" )
public String duplicateEx() {
return "Duplicate";
}通常引發異常時,會附帶以下類似的錯誤信息:
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
Cannot map 'fooMappingExamplesController' method
public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicateEx()
to {[/ex/foos/duplicate],methods=[GET]}:
There is already 'fooMappingExamplesController' bean method
public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicate() mapped.仔細閲讀錯誤信息表明,Spring 無法將方法<em>org.baeldung.web.controller.FooMappingExamplesController.duplicateEx()</em>映射成功,因為它與已映射的方法<em>org.baeldung.web.controller.FooMappingExamplesController.duplicate()</em>存在衝突。
以下代碼片段不會導致歧義映射錯誤,因為兩個方法返回不同的內容類型:
@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
return "<message>Duplicate</message>";
}
@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
return "{\"message\":\"Duplicate\"}";
}這種差異化允許我們的控制器根據請求中提供的 Accepts 標頭返回正確的表示形式。
另一種解決此方法的方法是更新兩個方法中分配的 URL。
7. 新的請求映射快捷方式
Spring Framework 4.3 引入了幾個新的 HTTP 映射註解,所有註解都基於 @RequestMapping。
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
這些新的註解可以提高代碼的可讀性和減少代碼冗餘。
讓我們通過創建支持 CRUD 操作的 RESTful API 來觀察這些新的註解。
@GetMapping("/{id}")
public ResponseEntity<?> getBazz(@PathVariable String id){
return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK);
}
@PostMapping
public ResponseEntity<?> newBazz(@RequestParam("name") String name){
return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK);
}
@PutMapping("/{id}")
public ResponseEntity<?> updateBazz(
@PathVariable String id,
@RequestParam("name") String name) {
return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteBazz(@PathVariable String id){
return new ResponseEntity<>(new Bazz(id), HttpStatus.OK);
}以下內容可以深入瞭解:
8. Spring 配置
Spring MVC 配置相對簡單,考慮到我們的 FooController 定義在以下包中:
package org.baeldung.spring.web.controller;
@Controller
public class FooController { ... }我們只需要一個 @Configuration 類來啓用完整的 MVC 支持並配置類路徑掃描,以用於控制器:
@Configuration
@EnableWebMvc
@ComponentScan({ "org.baeldung.spring.web.controller" })
public class MvcConfig {
//
}9. 結論
本文重點介紹了 Spring 中 @RequestMapping 註解的使用,討論了一個簡單的用例,包括 HTTP 頭部映射、使用 @PathVariable 將 URI 的一部分綁定以及與 URI 參數和 @RequestParam 註解的交互。
如果您想學習如何使用 Spring MVC 中的另一個核心註解,可以探索 @ModelAttribute 註解。