這一節雖然簡單,但是很繁瑣,可以先瞭解 @RequestMapping 的使用,不是很明白也沒有關係,先繼續往下學習,等你回頭再看一遍的時候,你會發現 @RequestMapping 竟然是如此的簡單! @RequestMapping @RequestMapping可以在控制器 類 上或者控制器 方 ...
這一節雖然簡單,但是很繁瑣,可以先瞭解 @RequestMapping 的使用,不是很明白也沒有關係,先繼續往下學習,等你回頭再看一遍的時候,你會發現 @RequestMapping 竟然是如此的簡單!
@RequestMapping
@RequestMapping可以在控制器類上或者控制器方法上使用。
在類的級別上的註解會將一個特定請求或者請求模式映射到一個控制器之上。之後你還可以另外添加方法級別的註解來進一步指定到處理方法的映射關係。
基礎用法:
下麵的 @RequestMapping("/index") 等同於 @RequestMapping(value = "/index")
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/index")
public class HelloWorldController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "/WEB-INF/views/success.jsp";
}
@RequestMapping(value = "/world", method = RequestMethod.POST)
public String world() {
return "/WEB-INF/views/success.jsp";
}
}
method
參數支持:GET, PUT, POST, DELETE 以及 PATCH。使用 method
可以限制接受的請求類型。
hello() 方法將只接受請求方式為 GET 方式,請求地址為:/index/hello 的請求。
world() 方法將只接受請求方式為 POST 方式,請求地址為:/index/world 的請求。
映射多個地址:
@RequestMapping
還可以將多個請求映射到一個方法上,只需要給 value
來指定一個包含多個路徑的列表。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class HelloWorldController {
@RequestMapping(value = {"/hello", "/world", "/helloworld"})
public String hello() {
return "/WEB-INF/views/success.jsp";
}
}
URI模板:
URI模板可以為快速訪問 @RequestMapping
中指定的URL的一個特定的部分提供很大的便利。
使用 @PathVariable
可以獲取到 {name} 的值,併在控制台進行輸出。比如請求地址為:http://localhost:8080/SpringMVC/hello/jack 那麼控制臺上將會把 jack 進行輸出。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}")
public String hello(@PathVariable String name) {
System.out.println(name);
return "/WEB-INF/views/success.jsp";
}
}
如果路徑中的URI變數和方法中的參數名不一樣的話,那麼需要在 @PathVariable
中顯示的綁定參數。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name") String username) {
System.out.println(username);
return "/WEB-INF/views/success.jsp";
}
}
一個方法可以擁有任意數量的 @PathVariable
註解:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}/age/{age}")
public String hello(@PathVariable String name, @PathVariable int age) {
System.out.println("name:" + name + ",age:" + age);
return "/WEB-INF/views/success.jsp";
}
}
@PathVariable
可以被應用於所有 簡單類型 的參數上,比如 int、long、Date 等類型。Spring會自動地幫你把參數轉化成合適的類型,如果轉換失敗,就拋出一個 TypeMismatchException
。如果你需要處理其他數據類型的轉換,也可以註冊自己的類。
帶正則表達式的URI模板:
你可以使用正則表達式來準確的描述可以接受的請求路徑:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name:[a-z]+}/age/{age}")
public String hello(@PathVariable String name, @PathVariable int age) {
System.out.println("name:" + name + ",age:" + age);
return "/WEB-INF/views/success.jsp";
}
}
Ant風格的路徑模式:
除了URI模板外,@RequestMapping
註解還支持Ant風格的路徑模式(如/hello/*.do
等)。不僅如此,還可以把URI模板變數和Ant風格的glob組合起來使用(比如/hello/*/user/{userId}
這樣的用法等)。其中*則表示任意字元串。但是遇到 / 那麼就會認為是下一部分的URI,所以 * 中不能有 / 。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
// 匹配的地址:http://localhost:8080/SpringMVC/hello/jack/user/18
@RequestMapping("/hello/*/user/{userId}")
public String hello(@PathVariable String userId) {
System.out.println(userId);
return "/WEB-INF/views/success.jsp";
}
}
那麼想要匹配帶 / 的路徑那該怎麼辦?可以使用 ** 來進行匹配。例如 /hello/**/user/{userId} 則會匹配 /hello/ 和 /user/{userId} 之間的部分。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
// 匹配的地址:http://localhost:8080/SpringMVC/hello/jack/tom/cat/user/18
@RequestMapping("/hello/**/user/{userId}")
public String hello(@PathVariable String userId) {
System.out.println(userId);
return "/WEB-INF/views/success.jsp";
}
}
路徑樣式的匹配(Path Pattern Comparison):
當一個URL同時匹配多個模板(pattern)時,我們將需要一個演算法來決定其中最匹配的一個。
URI模板變數的數目和通配符數量的總和最少的那個路徑模板更準確。舉個例子,/hotels/{hotel}/*
這個路徑擁有一個URI變數和一個通配符,而/hotels/{hotel}/**
這個路徑則擁有一個URI變數和兩個通配符,因此,我們認為前者是更準確的路徑模板。
如果兩個模板的URI模板數量和通配符數量總和一致,則路徑更長的那個模板更準確。舉個例子,/foo/bar*
就被認為比/foo/*
更準確,因為前者的路徑更長。
如果兩個模板的數量和長度均一致,則那個具有更少通配符的模板是更加準確的。比如,/hotels/{hotel}
就比/hotels/*
更精確。
除此之外,還有一些其他的規則:
- 預設的通配模式
/**
比其他所有的模式都更“不准確”。比方說,/api/{a}/{b}/{c}
就比預設的通配模式/**
要更準確 - 首碼通配(比如
/public/**
)被認為比其他任何不包括雙通配符的模式更不准確。比如說,/public/path3/{a}/{b}/{c}
就比/public/**
更準確
更多的細節請參考這兩個類:AntPatternComparator
和AntPathMatcher
。值得一提的是,PathMatcher類是可以配置的。
尾碼模式匹配:
Spring MVC預設採用 ".*"
的尾碼模式匹配來進行路徑匹配,因此,一個映射到/person
路徑的控制器也會隱式地被映射到 /person.*
。這使得通過URL來請求同一資源文件的不同格式變得更簡單(比如 /person.pdf
,/person.xml
)。
關閉尾碼模式匹配:
java:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
// 關閉尾碼模式匹配
.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(false)
.setUseRegisteredSuffixPatternMatch(true)
.setPathMatcher(antPathMatcher())
.setUrlPathHelper(urlPathHelper());
}
@Bean
public UrlPathHelper urlPathHelper() {
//...
}
@Bean
public PathMatcher antPathMatcher() {
//...
}
}
xml:
<mvc:annotation-driven>
<mvc:path-matching
<!-- 關閉尾碼模式匹配 -->
suffix-pattern="false"
trailing-slash="false"
registered-suffixes-only="true"
path-helper="pathHelper"
path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
矩陣變數:
矩陣變數可以在任何路徑段落中出現,每對矩陣變數之間使用一個分號 “;” 隔開。比如這樣的URI:"/cars;color=red;year=2012"
。多個值可以用逗號隔開 "color=red,green,blue"
,或者重覆變數名多次 "color=red;color=green;color=blue"
。
如果一個URL有可能需要包含矩陣變數,那麼在請求路徑的映射配置上就需要使用URI模板來體現這一點。這樣才能確保請求可以被正確地映射,而不管矩陣變數在URI中是否出現、出現的次序是怎樣等。在方法參數中使用 @MatrixVariable
來獲得矩陣變數中的值。
使用矩陣變數之前,需要在 springmvc 配置文件中開啟自動解析矩陣變數:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven enable-matrix-variables="true"/>
</beans>
下麵的代碼使用了矩陣變數:
使用 /pets/42;q=11;r=22
路徑來請求之後,控制臺上會輸出 petId:42,q:11
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable(name = "q", pathVar = "petId") int q) {
System.out.println("petId:" + petId + "," + "q:" + q);
}
}
由於任意路徑段落中都可以含有矩陣變數,在某些場景下,你需要用更精確的信息來指定一個矩陣變數的位置:
使用 /owners/42;q=11/pets/21;q=22
路徑來請求之後,控制臺上會輸出 q1:11,q2:22
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(name = "q", pathVar = "ownerId") int q1,
@MatrixVariable(name = "q", pathVar = "petId") int q2) {
System.out.println("q1:" + q1 + "," + "q2:" + q2);
}
}
你也可以聲明一個矩陣變數不是必須出現的,並給它賦一個預設值:
使用 /pets/42
路徑來請求之後,控制臺上會輸出 q:1
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(required = false, defaultValue = "1") int q) {
System.out.println("q:" + q);
}
}
可以觀察到,我們請求的路徑中並沒有 q 這個參數,配置了 required = false, defaultValue = "1"
之後,如果路徑中沒有指定的矩陣變數,那麼 SpringMVC 會自動給矩陣變數設置預設值。
也可以通過一個Map來存儲所有的矩陣變數:
使用 /owners/42;q=11;r=12/pets/21;q=22;s=23
路徑來請求之後,控制臺上會輸出 matrixVars:{q=11, r=12, s=23} 和 petMatrixVars:{q=22, s=23}
。
package com.pudding.controller;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable Map<String, String> matrixVars,
@MatrixVariable(pathVar = "petId") Map<String, String> petMatrixVars) {
System.out.println("matrixVars:" + matrixVars);
System.out.println("petMatrixVars:" + petMatrixVars);
}
}
consumes:
你可以指定一組可消費的媒體類型,縮小映射的範圍。這樣只有當請求頭中 Content-Type 的值與指定可消費的媒體類型中有相同的時候,請求才會被匹配。比如下麵這個例子:
@Controller
@RequestMapping(path = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
// 方法實現省略
}
指定可消費媒體類型的表達式中還可以使用否定,比如,可以使用 !text/plain 來匹配所有請求頭 Content-Type 中不含 text/plain 的請求。同時,在MediaType
類中還定義了一些常量,比如APPLICATION_JSON_VALUE
、APPLICATION_JSON_UTF8_VALUE
等,推薦更多地使用它們。
produces:
你可以指定一組可生產的媒體類型,縮小映射的範圍。這樣只有當請求頭中 Accept 的值與指定可生產的媒體類型中有相同的時候,請求才會被匹配。而且,使用 produces 條件可以確保用於生成響應(response)的內容與指定的可生產的媒體類型是相同的。舉個例子:
@RestController
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Pet getPet(@PathVariable String petId, Model model) {
// 方法實現省略
}
與 consumes 條件類似,可生產的媒體類型表達式也可以使用否定。比如,可以使用 !text/plain 來匹配所有請求頭 Accept 中不含 text/plain 的請求。同時,在MediaType
類中還定義了一些常量,比如APPLICATION_JSON_VALUE
、APPLICATION_JSON_UTF8_VALUE
等,推薦更多地使用它們。
請求參數與請求頭的值:
你可以篩選請求參數的條件來縮小請求匹配範圍,比如"myParam"
、"!myParam"
及"myParam=myValue"
等。前兩個條件用於篩選存在/不存在某些請求參數的請求,第三個條件篩選具有特定參數值的請求。下麵有個例子,展示瞭如何使用請求參數值的篩選條件:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// 實際實現省略
}
}
同樣,你可以用相同的條件來篩選請求頭的出現與否,或者篩選出一個具有特定值的請求頭:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(path = "/pets", method = RequestMethod.GET, headers="myHeader=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// 方法體實現省略
}
}
註:以上資料部分參考自W3Cschool翻譯的SpringMVC官方文檔