@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 的請求。

@GetMapping("/hello") 等同於 @RequestMapping(value="/hello", method=RequestMethod.GET)

@PostMapping("/world") 等同於 @RequestMapping(value="/world", method=RequestMethod.POST)

映射多個地址:

@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/**更准確

更多的細節請參考這兩個類:AntPatternComparatorAntPathMatcher。值得一提的是,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_VALUEAPPLICATION_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_VALUEAPPLICATION_JSON_UTF8_VALUE等,推薦更多地使用它們。

params:

你可以篩選請求參數的條件來縮小請求匹配范圍,比如"myParam""!myParam""myParam=myValue"等。前兩個條件用於篩選存在/不存在某些請求參數的請求,第三個條件篩選具有特定參數值的請求。下面有個例子,展示了如何使用請求參數值的篩選條件:

發送請求時必須帶上名字為myParam的參數,否則會報錯Parameter conditions "myParam" not met for actual request parameters:

@Controller
public class MyParamDemoController {

	@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "myParam")
	public void paramDemo() {
		// 省略方法體
	}

}

發送請求時不匹配帶有myParam的參數的請求,否則會報錯Parameter conditions "!myParam" not met for actual request parameters: myParam={helloworld}

@Controller
public class MyParamDemoController {

	@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "!myParam")
	public void paramDemo() {
		// 省略方法體
	}

}

可以用相同的條件來篩選請求頭的出現與否,或者篩選出一個具有特定值的請求頭,否則會報錯Parameter conditions "myParam=helloworld" not met for actual request parameters: myParam={123}

@Controller
public class MyParamDemoController {

	@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "myParam=helloworld")
	public void paramDemo() {
		// 方法體省略
	}

}

同樣的,params參數支持String類型的數組,這意味着你可以指定多個參數的限制:

@Controller
public class MyParamDemoController {

	@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = { "myParam=helloworld", "myParam2" })
	public void paramDemo() {
		// 省略方法體
	}

}

headers:

限制請求頭的參數

限制請求頭的參數User-Agent來讓谷歌瀏覽器不能訪問:

@Controller
public class MyHeaderDemoController {

	@RequestMapping(value = "/headers-demo", method = RequestMethod.GET, headers = "User-Agent!=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36")
	public void headersDemo() {
		// 省略方法體
	}

}

注:以上資料部分參考自W3Cschool翻譯的SpringMVC官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM