帶你進SpringMVC接受請求引數、
1. 接收請求引數
1.1. 【不推薦】通過HttpServletRequest
在處理請求的方法中,新增HttpServletRequest
物件作為引數,在方法體中,直接呼叫引數物件的getParameter()
或類似功能的方法,即可獲取請求引數:
@RequestMapping("handle_reg.do") public String handleReg( HttpServletRequest request) { System.out.println("UserController.handleReg()"); String username = request.getParameter("username"); String password = request.getParameter("password"); Integer age = Integer.valueOf(request.getParameter("age")); String phone = request.getParameter("phone"); String email = request.getParameter("email"); System.out.println("username=" + username); System.out.println("password=" + password); System.out.println("age=" + age); System.out.println("phone=" + phone); System.out.println("email=" + email); return null; }
1.2. 【推薦】在處理請求的方法中宣告同名引數
假設使用者提交的引數是username=root
,則引數名是username
,當需要獲取這個引數的值時,直接在處理請求的方法中宣告String username
即可,框架會把root
值直接用於呼叫處理請求的方法,即String username
的值就已經是root
了:
@RequestMapping("handle_reg.do") public String handleReg( String username, String password, Integer age, String phone, String email) { System.out.println("[2] username=" + username); System.out.println("[2] password=" + password); System.out.println("[2] age=" + (age + 1)); System.out.println("[2] phone=" + phone); System.out.println("[2] email=" + email); return null; }
使用這種做法時,可以無視資料型別,例如希望age
是Integer
型別的,則直接宣告為Integer
型別即可,無須自行轉換!
使用這種做法時,必須保證提交的請求引數的名稱,與處理請求的方法中的引數名稱是一致的!如果不一致,則處理請求的方法中,對應的引數值會是null
值!
如果引數名稱無法統一,後續有解決方案。
這種做法最大的缺陷是:不適用於資料專案太多的表單!否則,會導致處理請求的方法中需要新增大量的引數!
1.3. 【推薦】使用自定義型別獲取多項資料
假設請求引數中包含多項資料,例如:username=admin&password=123456&age=22&phone=13900139001&email=admin%40tedu.cn
@RequestMapping("handle_reg.do")
public String handleReg(User user) {
System.out.println("[3] username=" + user.getUsername());
System.out.println("[3] password=" + user.getPassword());
System.out.println("[3] age=" + (1 + user.getAge()));
System.out.println("[3] phone=" + user.getPhone());
System.out.println("[3] email=" + user.getEmail());
return null;
}
這種做法,適用於請求引數較多的場合!
注意:如果請求引數的引數名稱,與類中的屬性名稱不一致,則類物件中對應的屬性值為null
!
注意:這種做法可以與前序介紹的第2種做法組合來使用!
1.4. 小結
關於獲取請求引數,首先,並不推薦使用HttpServletRequest
,主要原因是相對比較原始,編碼比較繁瑣!而宣告同名引數,或宣告物件,都是推薦的做法,至於使用哪一種,可以根據引數的數量及資料是否適合被封裝到同一個類中,綜合評定,並且,這2種做法可以組合使用!
2. 控制器的響應
2.1. 常見的響應方式
【轉發】在轉發過程中,客戶端只發出過1次請求!在瀏覽器的位址列中,也只會顯示第1次請求的路徑!轉發是在伺服器內部完成的,可以傳遞資料!
【重定向】當伺服器響應重定向時,客戶端會發出第2次請求!最終,在瀏覽器的位址列中,會顯示第2次請求的路徑!由於是2次不同的請求,基於Http協議是無狀態協議,沒有經過特殊處理(Session/Cookie/資料庫存取……)的資料是無法在2次請求之間傳遞的!
2.2. 常見的響應碼
被伺服器接收到的每個請求,在最終響應時,伺服器端都會給出一個響應碼,例如200
、404
等。通常:
- 2xx:正確的響應,例如
200
、206
等…… - 3xx:重定向,例如
302
、301
等…… - 4xx:請求錯誤,例如請求的資源不存在,或者請求型別錯誤、或者請求引數錯誤等等,例如
400
、404
、405
、406
等…… - 5xx:伺服器內部錯誤,通常可能是出現某種異常,例如
500
等……
3. 轉發資料
3.1. 【不推薦】將轉發的資料封裝在HttpServletRequest物件中
可以為處理請求的方法新增HttpServletRequest request
引數,當需要轉發資料時,將資料封裝在request
中即可,後續也不需要顯式的執行轉發,在SpringMVC的控制器中,預設的響應方式就是轉發。
@RequestMapping("handle_reg.do")
public String handleReg(User user,
HttpServletRequest request) {
// 假定輸入的使用者名稱已經被佔用
// 提示:您輸入的使用者名稱XXX已經被佔用
request.setAttribute("msg",
"您輸入的使用者名稱" + user.getUsername() + "已經被佔用!");
// 返回檢視名,也可以理解為檔案的檔名
return "error"; // 頁面:/WEB-INF/error.jsp
}
3.2. 【不推薦】使用ModelAndView
可以將處理請求的方法的返回值設定為ModelAndView
型別,該型別的常用構造方法有:
ModelAndView()
ModelAndView(String viewName)
ModelAndView(String viewName, Map<String, ?> model)
當需要轉發資料時,可以使用以上3種中的最後一種:
@RequestMapping("handle_reg.do")
public ModelAndView handleReg(String username) {
String viewName = "error";
Map<String, Object> model
= new HashMap<String, Object>();
model.put("msg",
"[2] 您輸入的使用者名稱" + username + "已經被佔用!");
ModelAndView mav
= new ModelAndView(viewName, model);
return mav;
}
由於這種方式使用相對比較複雜,所以,一般不推薦使用這種做法!
3.3. 【推薦】使用ModelMap封裝需要轉發的資料
使用ModelMap
的流程與使用HttpServletRequest
完全相同,即:方法的返回值依然使用String
型別,在方法中宣告該引數,然後在方法體中直接封裝資料,最後,返回檢視名:
@RequestMapping("handle_reg.do")
public String handleReg(String username,
ModelMap modelMap) {
modelMap.addAttribute("msg",
"[3] 您輸入的使用者名稱" + username + "已經被佔用!");
return "error";
}
3.4. 小結
在SpringMVC中,轉發資料共有3種做法,第1種使用HttpServletRequest
的做法簡單直接,但是,並不推薦這樣處理,主要是因為框架已經幫我們處理了request需要執行的任務,而我們在編寫程式碼時應該儘量不干預框架的處理過程,第2種使用ModelAndView
,是框架的核心處理方式,但是,因為使用方式過於麻煩,所以,也不推薦這樣使用,第3種使用ModelMap
,使用簡潔,推薦使用。
3.5. 附:重定向
在SpringMVC中,當需要重定向時,首先,應該保證處理請求的方法的返回值是String
型別(與轉發一樣),然後,返回值使用redirect:
作為字首即可,例如:
@RequestMapping("handle_reg.do")
public String handleReg() {
// 假設註冊成功,需要登入
return "redirect:login.do";
}
需要注意的是:在redirect:
右側的不是檢視名,而是重定向的目標的路徑,可以是絕對路徑,也可以是相對路徑。
當處理的請求的返回值型別是String時,如果返回值使用redirect:作為字首,是重定向,否則,是轉發!
4. 關於@RequestMapping註解
通過配置@RequestMapping
,可以繫結請求路徑與處理請求的方法,例如:
@RequestMapping("login.do")
public String showLogin() { ...
即:通過以上配置,當接收到login.do
請求時,SpringMVC會自動呼叫showLogin()
方法。
除了在方法之前新增該註解以外,該註解還可以新增在控制器類的宣告之前,例如:
@RequestMapping("user")
@Controller
public class UserControler { ...
當方法之前添加了該註解之後,方法內配置的所有請求路徑,在最終訪問時都必須新增user
路徑,例如:http://localhost:8080/SPRINGMVC-02-USER/user/reg.do
。
通常,推薦在類之前也新增該註解,方便管理路徑,例如在某個新聞管理的應用中,可能存在news_list.do
、news_info.do
的請求,而在這個應用中,也會有使用者資料,就存在user_list.do
、user_info.do
,可以發現,為了保證請求路徑是唯一的,都需要在路徑之前新增xxx_
作為字首,這樣的管理方式是非常不方便的,在類之前新增@RequestMapping
註解就可以很好的解決這個問題,每個路徑之前根本就不需要配置字首字元,也不會發生衝突!
在@RequestMapping
的使用過程中,路徑可以使用/
作為第1個字元,也可以不需要這個字元,例如:
/user /login.do
user login.do
/user login.do
user /login.do
以上4種配置都是正確的!通常,推薦使用/
作為第1個字元,即以上第1種方式!
除了配置請求路徑以外,使用@RequestMapping
還可以限制請求方式,即某個路徑可以設定為只允許POST
請求,而不接收GET
請求!
【GET】會將請求的引數與值體現在URL中;請求的引數與值會受到URL長度限制,不適用於傳遞大量的資料;
【POST】請求的引數與值不會體現在URL中;可以傳遞大量的資料;
【選取】請求的引數與值涉及隱私(例如密碼)則必須使用POST;資料量可能較大時必須使用POST;需要共享URL且其中包含引數時必須使用GET;支援頁面重新整理必須使用GET。
【複雜度】如果要發出POST請求,只能通過<form>
中的<input type="submit" />
或<button />
,或者通過JS技術,否則,在Web領域無法發出POST請求,而這2種方式也都可以用於發出GET請求,除此以外,直接在瀏覽器中輸入某個URL發出的也是GET請求,總的來說,發GET請求要簡單得多。
【小結】參考以上“選取”原則,選擇請求方式,如果兩者均可,則使用GET即可。
在@RequestMapping
中配置method
屬性可以限制請求型別:
@RequestMapping(value="handle_reg.do",
method=RequestMethod.POST)
public String handleReg() {
例如以上程式碼限制了handle_reg.do
必須通過POST方式來請求,如果使用GET方式,則會返回405錯誤!
只有需要限定請求方式時,才需要顯式的配置value="handlereg.do",否則,直接將"handlereg.do"配置在註解中即可!
小結:關於@RequestMapping
註解,主要作用是配置請求路徑,推薦在控制器類和處理請求的方法之前都新增該註解,類之前的註解是用於配置請求路徑中的層次,方法之前的註解是用於配置請求的資源,關於路徑的配置是該屬性的value
屬性,如果只配置請求路徑,可以不用顯式的宣告這是配置value
屬性,而是直接把值寫出來即可,例如不需要寫成@RequestMapping(values="login.do")
,而可以直接寫成@RequestMapping("login.do")
,在配置路徑時,推薦使用/
作為第1個字元,例如@RequestMapping("/login.do")
,如果還需要限制請求方式,則必須顯式的宣告路徑為value
屬性的值,並且新增配置method
屬性,例如:@RequestMapping(value="handle_reg.do", method=RequestMethod.POST)
。
5. 關於@RequestParam註解
使用@RequestParam
註解,可以解決請求引數名稱與處理請求的方法的引數名稱不一致的問題,例如:
public String handleLogin(
@RequestParam("name") String username,
String password) { ...
則請求引數的名稱是name
,而處理請求的方法中的引數名稱卻是username
,這是可以正常執行的!
一旦使用了@RequestParam
註解,預設情況下,引數就是必須的!例如配置了@RequestParam("passwd") String password
後,如果請求中並不存在名為passwd
的引數,則會出現400錯誤:
HTTP Status 400 - Required String parameter 'passwd' is not present
沒有提交名為passwd的引數,與提交了空值,是兩碼事!即:如果提交了passwd引數卻沒有值(例如輸入框中沒有輸入值),在伺服器將得到空字串(""),程式並不會出現錯誤!如果根本就沒有提交名為passwd的引數,則會導致400錯誤!
如果使用了@RequestParam
註解,卻又不想設定為必須提交該引數,可以:
@RequestParam(value="name", required=false)
則將根據name
去接收引數,如果有值,會正確接收,如果沒有(沒有提交該名稱的引數),則會是null值!
當required=false
時,意味著可以不必提交該引數,還可以多配置一項defaultValue
屬性(The default value to use as a fallback when the request parameter value is not provided or empty. Supplying a default value implicitly sets required() to false.
),表示如果請求中沒有提交該引數,則預設值是多少!例如:
@RequestParam(value="passwd", required=false, defaultValue="888999") String password
以上程式碼表示:希望請求中包含名為passwd
的引數,如果有,則值用於方法的String password
的引數,如果沒有,也不是必須要提供(required=false
),並且使用"888999"
作為預設值(defaultValue="888999"
),即:在這種情況下,String password
的值是"888999"
。
小結:@RequestParam
註解是用於處理請求的方法中的引數之前,可以配置3項屬性,分別是value
表示請求引數名稱,required
表示請求中是否必須包含該引數,defaultValue
表示引數的預設值,當有以上任何一種需求時,都需要配置該註解,即:請求引數名稱與處理請求的方法的引數名稱不一致;強制必須提交某個引數;為某個引數配置預設值。