文章目錄
- 系列文章目錄
- 前言
- 一、RequestMapping注解
- 1.1 value屬性
- 1.2 method屬性
- 1.3 params屬性
- 1.4 header屬性
- 1.5 通配符
- 二、PathVariable 注解
- 三、REST風格的URL
- 3.1 REST簡介
- 3.2 REST風格URL的使用
- 3.3 HiddenHttpMethodFilter原始碼分析
- 四、引數傳入
- 4.1 基本型別引數的獲取
- 4.2 自定義型別引數的獲取
- 五、中文亂碼的解決
- 5.1 請求亂碼
- 1. GET請求
- 2. POST請求
- 5.2 回應亂碼
- 六、使用原生API
系列文章目錄
SpringMVC學習指南(1)
關注博主,學習后續知識

前言
提示:
本博客的內容是在上篇博客SpringMVC學習指南(1)的基礎上更新的,沒有看過上一篇的同學請移步看完上一篇博客之后,再進行本篇博客的閱讀.
本篇博客的會用大量的代碼實體帶你快速掌握SpringMVC開發的幾個基本且重要的注解,希望大家在看博客的同時,多動手敲,多除錯,便能更好的學會SpringMVC,
一、RequestMapping注解
該注解有四個常用屬性,分別是:
| 屬性名 | 型別 | 含義 |
|---|---|---|
| value | string[] | 請求映射 |
| method | RequestMethod[] | 請求方式 |
| params | string[] | 請求引數 |
| header | string[] | 請求頭 |
1.1 value屬性
(1)含義
- 指定請求映射路徑,SpringMVC中一個方法處理一個請求,這里的這個請求映射路徑就對應前端發起的某個請求,
(2)有兩個作用:
- 標注在類上:為下面的方法設定了一個基準路徑,即:類下的所有方法都會有一個前綴地址,
- 標注在方法上:對應一個請求路徑,即,該方法要處理的是哪個請求,
(3)實體:
在前端頁面點擊超鏈接,發起請求 /hello ,后端myFirstRequest()方法上標注了 @RequestMapping(value
= “/hello”) 注解,就表示該方法是用來處理 /hello 請求的,
處理完成之后,方法會回傳一個字串 “success”,我們配置的視圖決議器(InternalResourceViewResolver)會完成拼串操作,即:會轉發到一個success.jsp頁面,
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<html>
<body>
<h1>首頁</h1>
<a href="hello">HelloWorld測驗</a><hr>
</body>
</html>
@RequestMapping(value = "/hello")
public String myFirstRequest()
{
System.out.println("hello請求正在處理……");
//視圖決議器自動拼串,拿到它的前后綴,
//轉發到 /WEB-INF/pages/success.jsp 頁面
return "success";
}
1.2 method屬性
(1)含義
- 限定請求方式(GET請求、POST請求),默認是都接收,不區分GET和POST,
- 如果設定了method的值,就只支持設定的請求方式,如果以其他的方式發起的請求,瀏覽器就會報錯 405-Method Not Allowed
(2)method的四個取值
- RequestMethod.GET
- RequestMethod.POST
- RequestMethod.DELETE
- RequestMethod.PUT
- 這些請求方式有什么用呢?
在后文中我們會使用RUST風格的URL,到時候就會使用不同的請求方式發起請求,同一個URL,它發起的請求方式不同,后端的處理方式就不同,
(3)實體
當然,這里設定method方式為GET是多此一舉的,要求前端發起的超鏈接請求本就是GET方式的,這里只是為了說明如何使用,
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String myFirstRequest()
{
System.out.println("請求正在處理…… success");
return "success";
}
1.3 params屬性
(1)含義
-
規定請求引數,請求路徑中必須有引數,如:?username=1
-
params屬性 和 header屬性 支持簡單的運算式,
- param:表示請求必須包含名為 param 的引數
- !param: 表示請求不能包含名為 param 的引數
- param != value : 表示請求必須包含名為 param 的引數,但其值不能為value
-
eg:
- params = {“username”} :表示發送請求必須帶上一個名為username的引數,請求路徑中必須有引數,如:/hello?username=1
- params = {"!username"}:表示請求不能包含名為username的引數,
- params = {“username=123”},請求中的引數username必須是123,
- params = {"!username=123",“pws=321”},路徑中引數之間使用 & 連接,
1.4 header屬性
(1)含義
- 規定請求頭
(2)作用:
- 限定哪些瀏覽器可以訪問,
-
User-Agent:瀏覽器資訊,可以規定哪些瀏覽器可以訪問,哪些不可以訪問
- 谷歌瀏覽器的User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 - IE瀏覽器的User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362
- 谷歌瀏覽器的User-Agent:
-
@RequestMapping(value = "/handle04",headers = {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"})
public String handle04()
{
return "success";
}
1.5 通配符
RequestMapping的模糊匹配 Ant風格的URL,使用通配符
URL地址中可以以下四個寫通配符
?:能替代任意一個字符*:能替代任意多個字符(包括0個字符),和一層路徑**: 能替代多層路徑
注意:當模糊匹配和精確匹配同時存在時,精確匹配優先,
代碼示例:
@Controller
public class RequestMappingTest {
@RequestMapping("/antTest01")
public String antTest01()
{
System.out.println("antTest01");
return "success";
}
/**
* 只要請求路徑是 /antTest0 開頭,只要最后一個字符不同,
* 模糊和精確多個匹配的情況下,精確匹配優先
* @return
*/
@RequestMapping("/antTest0?")
public String antTest02()
{
System.out.println("antTest02");
return "success";
}
@RequestMapping("/antTest0*")
public String antTest03()
{
System.out.println("antTest03");
return "success";
}
}
二、PathVariable 注解
拿到請求路徑中占位符的資訊,
路徑上可以有占位符:
- 占位符的使用,語法:{變數名}
- 占位符只能占一層路徑
eg:
<a href="user/LXY">路徑屬性值測驗</a><br/>
@RequestMapping("/user/{name}")
public String pathVariableTest(@PathVariable("name") String name)
{
System.out.println("路徑上的占位符的值為"+name);
return "success";
}
三、REST風格的URL
3.1 REST簡介
REST:即 Representational State Transfer,(資源)表現層狀態轉化,是目前最流行的一種互聯網軟體架構,它結構清晰、符合標準、易于理解、擴展方便,所以正得到越來越多網站的采用
-
資源(Resources):網路上的一個物體,或者說是網路上的一個具體資訊,它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在,可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的 URI ,要獲取這個資源,訪問它的URI就可以,因此 URI 即為每一個資源的獨一無二的識別符,
-
表現層(Representation):把資源具體呈現出來的形式,叫做它的表現層(Representation),比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以采用二進制格式,
-
狀態轉化(State Transfer):每發出一個請求,就代表了客戶端和服務器的一次互動程序,HTTP協議,是一個無狀態協議,即所有的狀態都保存在服務器端,因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生“狀態轉化”(State Transfer),而這種轉化是建立在表現層之上的,所以就是 “表現層狀態轉化”,具體說,就是 HTTP 協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE,它們分別對應四種基本操作:(請求方式)
-
GET用來獲取資源 -
POST用來新建資源 -
PUT用來更新資源 -
DELETE用來洗掉資源,
比如:
發送的請求是 “/book”:
- 如果是以GET方法發送的,就認為是獲得book,
- 如果是以DELETE方式發送的,就認為是洗掉book,
通俗來說:
REST風格是希望以非常簡潔的URL地址來發送請求,用請求方式來區分對一個資源的增刪改查,
3.2 REST風格URL的使用
(1)我們在JavaWeb中寫的URL
-
/getBook?id=1 查詢id為1的圖書
-
/deleteBook?id=1 洗掉id為1的圖書
(2)REST風格的URL
- 格式: /資源名/資源識別符號
- eg:URL為:
/book/1- GET:查詢1號圖書
- PUT:更新1號圖書
- DELETE:洗掉1號圖書
(3)存在的問題
頁面只能發起GET和POST請求,其他方法的請求無法發起
-
解決: Spring提供了REST風格的支持
- SpringMVC有一個Filter,他可以把普通的請求轉換為規定形式的請求,
- 在web.xml 中配置Fitter,(HiddenHttpMethodFilter)
- SpringMVC有一個Filter,他可以把普通的請求轉換為規定形式的請求,
<!-- 配置filter,將POST請求轉換為其他合適的請求方式,如DELETE,PUT-->
<filter>
<filter-name>filter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<!-- 過濾所有請求,只有Servlet中寫的是“/”,其他地方的url寫的還是“/*”-->
<url-pattern>/*</url-pattern>
</filter-mapping>
- 如何發起其他形式的請求?
- 創建一個
POST型別的表單 - 表單項(input標簽)中攜帶一個
_method的引數, - 這個
_method的值(value)就是DELETE或者PUT
- 創建一個
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<html>
<body>
<h2>圖書的增刪改查,使用REST風格的URL地址</h2>
<a href="book/1">查詢圖書</a>
<form action="book" method="post">
<input type="submit" value="保存圖書">
</form>
<%--Delete請求--%>
<form action="book/1" method="post">
<input name="_method" value="DELETE">
<input type="submit" value="洗掉圖書">
</form>
<%--Put請求--%>
<form action="book/1" method="post">
<input name="_method" value="PUT">
<input type="submit" value="更新圖書">
</form>
</body>
</html>
@Controller
public class BookController {
@RequestMapping(value = "/book",method = RequestMethod.POST)
public String addBook()
{
System.out.println("添加圖書成功");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.DELETE)
public String deleteBook(@PathVariable("bookId") Integer id)
{
System.out.println("已經洗掉了【 "+id+" 】號圖書");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.PUT)
public String updateBook(@PathVariable("bookId") Integer id)
{
System.out.println("已經更新了【 "+id+" 】號圖書");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.GET)
public String getBook(@PathVariable("bookId") Integer id)
{
System.out.println("已經查詢到了【 "+id+" 】號圖書");
return "success";
}
}
3.3 HiddenHttpMethodFilter原始碼分析
(1)HiddenHttpMethodFilter的底層實作:
- 確定請求是POST,
- 從請求中拿到“_method”對應的value的值,并轉換為大寫,
- new出一個新的“HttpServlet物件”即:HttpMethodRequestWrapper,將新的method設定為其新的請求型別,
- 并放行這個新的“HttpServlet”物件,
HiddenHttpMethodFilter部分原始碼:
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
//拿到_method 對應的值,
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
(2)高版本的Tomcat可能會出現的問題
在上面的程式中可能會出現如下錯誤:
-
原因:
- 高版本的Tomcat對jsp頁面有約束,Tomact 8.0之后
-
解決:
- 在需要跳轉到的jsp頁面中加上對例外的支持
上面的程式執行完畢之后是要跳轉到 success.jsp 頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>成功頁面</title>
</head>
<body>
<h2>成功!!!</h2>
</body>
</html>
四、引數傳入
問:什么叫引數傳入?
答:引數傳入,即:如何獲取前端頁面(表單等)提交的引數資訊,并在后端處理方法中使用,
4.1 基本型別引數的獲取
(1)默認方式
- 默認方式獲取請求引數,只需給方法引數上寫一個和前度葉面引數名相同的變數,這個變數就自動來接收請求引數的值,
- 有值:則直接獲取
- 無值:null
(2)使用注解
-
@RequestParam:獲取表單中提交的引數
-
該注解相當于原生Servlet中的:
request.getParameter(key) -
eg:
public String test01(@RequestParam(value = "username",required = false,defaultValue = "Hello") String name)
{
}
相當于:name=request.getParameter("username);
-
注意:如果直接使用該注解,則默認請求中必須帶要獲取的引數名,不帶就報錯,可以使用注解的屬性改變,
- value:默認,獲取指定key的引數
- required:是否必須指定,布爾型別
- defaultValue:默認值,原本默認是null,現在可以指定默認值
-
注意和 @PathVariable的區別:
@PathVariable是獲取路徑中的key對應的值@PathVariable("user")獲取/book/{user}@RequestParam("user")獲取前端提交的引數,例如:form表單中的user引數,
(3)擴展學習兩個注解
@RequestHeader 和 @CookieValue
-
@RequestHeader: 獲取請求頭中的值,- 相當于:原生Servlet:
request.getHeader("key"); - 如果請求頭中沒有這個key,會報500錯誤,
- 該注解也有
value,required,defaultValue屬性值,其使用方法和 @RequestParam 中的三個屬性一樣,
- 相當于:原生Servlet:
-
@CookieValue:獲取某個cookie的值,- 相當于:原生Servlet:
Cookies[] cookies = request.getCookies();,只不過原生Servlet中獲取的是所有的cookie,然后在遍歷拿到真正需要的那個cookie, - 該注解也有三個屬性:required,defaultValue,defaultValue,同上,
- 相當于:原生Servlet:
看一段代碼來熟悉一下這三個注解
前端頁面
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" isErrorPage="true"%>
<html>
<body>
<h2>測驗獲取請求引數</h2>
<a href="testParam?username=Tom">獲取引數</a><br/>
<a href="testHeader">獲取請求頭資訊</a><br/>
<a href="testCookies">獲取Cookies資訊</a><br/>
</body>
</html>
controller代碼
package nuc;
/**
* 獲取請求帶來的引數
*/
@Controller
public class RequestParamsController {
/**
*
* @return 成功頁面
*/
@RequestMapping(value = "/testParam")
public String testRequest01(@RequestParam(value = "username",required = false,defaultValue = "Hello") String name)
{
System.out.println("請求引數:"+name);
return "success";
}
@RequestMapping(value = "/testHeader")
public String testRequest02(@RequestHeader("User-Agent") String userAgent)
{
System.out.println("請求頭資訊:"+userAgent);
return "success";
}
@RequestMapping(value = "/testCookies")
public String testRequest03(@CookieValue("JSESSIONID") String jsessionid)
{
System.out.println("Cookies 資訊:"+jsessionid);
return "success";
}
}
4.2 自定義型別引數的獲取
(1)概念
-
如果我們方法的引數是一個物體類,SpringMVC會自動為這個物體類賦值,
-
將這個物體類中的每一個屬性(要求和前端頁面提交的引數的名椅子),從request中獲取出來,進行封裝,
-
還可以級聯封裝,即:這個物體類中還有另外一個物體類,(在請求中的引數也必須是級聯的)
-
注意:這個物體類的屬性值的名要和請求中的引數名一致,否則會獲取不到這個屬性值,即為null,
-
(2)實踐
前端頁面:
<form action="addBook" method="post">
書名:<input type="text" name="name">
作者:<input type="text" name="author">
價格:<input type="text" name="price">
庫存:<input type="text" name="stock">
<hr/>
<input type="text" name="address.province">
<input type="text" name="address.city">//級聯屬性
<input type="submit">
</form>
Book物體類
public class Book {
private String bookname;
private String author;
private Double price;
private int stock;
private Address address;
}
@RequestMapping("/addBook")
public String addBook(Book book)
{
System.out.println(book);
return "success";
}
這個addBook() 方法中的book,就會和前端頁面提交的引數進行一對一系結,(前提是引數名一致)
五、中文亂碼的解決
5.1 請求亂碼
1. GET請求
改Tomcat的server.xml檔案,在如圖位置8080后,加上:URIEncoding=“UTF-8” (我這里并沒有修改web.xml檔案,大家如果沒有遇到過GET請求中文亂碼的話,就不必修改此處)
2. POST請求
- 在JavaWeb中的處理方式是,在第一次獲取請求引數之前設定:
response.setCharacterEncoding("UTF-8"); - 在SpringMVC中有一個專門的過濾器,它專門用來處理字符亂碼,
CharacterEncodingFilter - 在SpringMVC的組態檔中配置該過濾器,
<!--配置一個過濾字符編碼方法的Filter,來處理字符亂碼-->
<!--該Filter必須在所有的Filter之前,否則不會有過濾效果-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 設定POST請求編碼方式,其實默認就是UTF-8,只不過我們可以使用init-param標簽類設定-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--設定回應編碼-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 注意:該過濾器要直接寫在“前端控制器”的Servlet之后,即:在其他所有的Filter之前,否則會報錯!!!
- 原因:因為在JavaWeb中我們就知道,在設定字符編碼時,就要在第一次獲取引數之前設定,所以,該過濾器就就必須在其他過濾器之前,否則無法起到很好的字符編碼過濾,SpringMVC就會發出報錯資訊,
5.2 回應亂碼
對回應亂碼的處理非常簡單,使用一行代碼即可
response.setContentType("text/html;charset=utf-8");
六、使用原生API
這里“使用原生API”的意思就是,在SpringMVC的控制器方法引數串列中使用JavaWeb中的API,即:
- HttpServletResponse:
- HttpServletRequest:
- HttpSession:
還有一些,只不過這三個是常用的,其他的都不常用,這里就不再贅述了
原生API的使用,我們在JavaWeb中已經很熟悉了,這里就直接用一段代碼來復習一些,
什么?你竟然沒有了解過Servlet?🤦?♂?🤦?♂?🤦?♂?,
也不要緊,先關注一波博主,等我整理完SpringMVC的知識點,就開始上傳Servlet的有關東西,傳送門【博主】
@RequestMapping("/servletApi")
public String addAPI(HttpSession session, HttpServletRequest request)
{
request.setAttribute("requestParam","我是Request中的引數");
session.setAttribute("sessionParam","我是session中的引數");
return "success";
}
在success.jsp 頁面使用EL運算式獲取我們放入原生API中的資料(前提是匯入JSTL的包)
<html>
<head>
<title>成功頁面</title>
</head>
<body>
<h2>成功!!!</h2>
<hr>
Request引數:${requestScope.requestParam}
Session引數:${sessionScope.sessionParam}
</body>
</html>
移步博主首頁
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/198271.html
標籤:AI
上一篇:2020-10-31
