SpringMVC之請求參數的擷取方式
常見的一個web服務,如何擷取請求參數?
一般最常見的請求為GET和POST,get請求的參數在url上可以擷取,post請求參數除了url上還有可能在表單中,檔案上傳時,擷取方式又和一般的參數擷取不一樣
本篇則主要集中在不同請求方式下,擷取參數的使用姿勢
首先需要搭建一個後端的請求,為了快速示範
利用spring-boot建立了一個機器簡單的工程,依賴版本
1.5.4.RELEASE
I. GET請求參數擷取
get請求參數,一般都是直接挂在請求的url上,是以擷取這些參數還是比較簡單的
1. 通過 HttpServletRequest擷取參數
這個可以說是最基本最常見的的方式了,
javax.servlet.ServletRequest#getParameter
來擷取對應的參數,下面各處一個執行個體
@RestController
@RequestMapping(path = "webs/demo")
public class DemoController {
@RequestMapping(path = "req1")
public String req1(HttpServletRequest request) {
String user = request.getParameter("user");
String password = request.getParameter("password");
return "req1 user: " + user + " pwd: " + password;
}
}
根據上面暴露的接口,我們測試的case就很簡單了
http://127.0.0.1:8080/webs/demo/req1?user=小灰灰Blog&password=123456
## 輸出 req1 user: 小灰灰Blog pwd: 123456
http://127.0.0.1:8080/webs/demo/req1?user=小灰灰Blog
## 輸出 req1 user: 小灰灰Blog pwd: null
說明
- 這是一個最基本的擷取參數的方式,get,post請求都适用的,通常在filter,intercepter中也是可以通過
對象來擷取請求參數HttpServletRequest
- 除了擷取常見的請求參數之外,
可以擷取請求頭的完整資訊HttpServletRequest
- 在一次請求的生命周期内,可以通過下面的方式擷取Request對象(當然也可以擷取response對象)
HttpServletRequest httpServletRequest = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
2. 直接方法參數擷取
直接給出case, 這個方法依然是放在上面的DemoController下面的
@RequestMapping(path = "req2")
public String req2(String user, String password) {
return "req2 user: " + user + " pwd: " + password;
}
請求驗證
http://127.0.0.1:8080/webs/demo/req2?user=%E5%B0%8F%E7%81%B0%E7%81%B0Blog&password=123456
## 輸出: req2 user: 小灰灰Blog pwd: 123456
http://127.0.0.1:8080/webs/demo/req2?password=123456
## 輸出: req2 user: null pwd: 123456
http://127.0.0.1:8080/webs/demo/req2?password=123456&User=blog
## 輸出: req2 user: null pwd: 123456
注意:
- 上面這種使用方式,相當于直接将url參數映射到了Controller方法的參數上了
- 方法參數名必須和url參數名完全一緻(區分大小寫)
- 順序無關
- 若參數沒傳,則預設為null
一個疑問
上面的demo中Controller的方法參數都是String還好,如果将password改成int,會出現什麼情況
代碼稍作修改
@RequestMapping(path = "req2")
public String req2(String user, int password) {
return "req2 user: " + user + " pwd: " + password;
}
實際測試
# case1
http://127.0.0.1:8080/webs/demo/req2?password=123456&user=blog
## 輸出: req2 user: blog pwd: 123456
# case 2
http://127.0.0.1:8080/webs/demo/req2?password2=123456&user=blog
## 輸出: 報錯, Optional int parameter 'password' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type
# case 3
http://127.0.0.1:8080/webs/demo/req2?password=abc&user=blog
## 輸出:報錯, "Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "abc""
結果說明
- 如果請求參數與方法參數類型不一緻,會抛出轉換異常
- 如果方法參數為非封裝基本類型,則url參數必須存在,否則報錯
3. RequestParam注解方式擷取請求參數
通過
@RequestParam
注解擷取參數的方式和上面的一種比較類似,case如下
@RequestMapping(path = "req3", method = RequestMethod.GET)
public String req3(@RequestParam("user") String username,
@RequestParam("password") String pwd) {
return "req3 user: " + username + " pwd: " + pwd;
}
測試case
# case1
http://127.0.0.1:8080/webs/demo/req3?password=123456&user=blog
## 輸出: req3 user: blog pwd: 123456
# case2
http://127.0.0.1:8080/webs/demo/req3?password=123456
## 輸出:報錯, Required String parameter 'user' is not presen
說明
- 不指定注解的name或value屬性時,等同于第二種使用姿勢
- 注解的name屬性或value屬性,用實際的參數名來指定
- controller的參數名與url參數名沒有強關聯(差別第二種方式)
- 參數類型需要保證一緻(通第二種方式)
-
如果url參數可選,請設定require屬性為false,如下
java @RequestParam(name = "user", required = false) String username
4. Bean方式擷取參數
對于請求參數比較複雜的情況下,我比較喜歡這種使用姿勢,管理起來友善簡單
@Data
public static class UserDO {
String user;
String password;
}
@RequestMapping(path = "req4", method = RequestMethod.GET)
public String req4(UserDO userDO) {
return "req4 userDO: " + userDO;
}
測試case
# case1
http://127.0.0.1:8080/webs/demo/req4?password=123456&user=%E5%B0%8F%E7%81%B0%E7%81%B0Blog
## 輸出: req4 userDO: DemoController.UserDO(user=小灰灰Blog, password=123456)
# case2
http://127.0.0.1:8080/webs/demo/req4?password=123456
## 輸出: req4 userDO: DemoController.UserDO(user=null, password=123456)
說明
- 定義一個bean,内部屬性和請求參數對應
- 允許參數不存在的情況,會使用null代替(是以,盡量不要使用非封裝基本類型,否則參數不傳時,會抛異常)
- bean的屬性,可以根據實際情況指定類型
5. ModelAttribute注解方式
@ModelAttribute注解的方法,會優于Controller之前執行,一般更常見于向視圖傳輸資料使用,此處不詳細展開,正常來講,專門的擷取參數不太會用這這種方式來玩
6. Path參數
Path參數,專指的是請求路徑的參數,如
http://127.0.0.1:8080/webs/demo/req4?password=123456
上面這個url中,password是我們傳統意義上的請求參數,其中path參數則是指其中
req4
,
demo
這種path路徑中的一環;對此,最常見的一個case就是常見的部落格中,如開源中國的一個部落格連結
https://my.oschina.net/u/566591/blog/1601400
- 566591 : 這個參數主要用來區分使用者
- 1601400 : 這個參數則主要是表示對應的博文
一般path參數的擷取方式如下
@RequestMapping(path = "req6/{user}/info")
public String req6(@PathVariable(name = "user") String user) {
return "req6 user: " + user;
}
測試case
# case1
http://127.0.0.1:8080/webs/demo/req6/blog/info?user=haha
## 輸出:req6 user: blog
# case2
http://127.0.0.1:8080/webs/demo/req6/blog?user=haha
## 輸出: 404
# case3
http://127.0.0.1:8080/webs/demo/req6/info?user=haha
## 輸出: 404
注意:
- path參數的使用,需要確定參數存在且類型比對
- path參數和url參數不會互相影響
II. POST請求參數擷取
POST請求參數,更多的是看送出表單參數是否可以擷取到,以及如何擷取,主要的手段依然是上面幾種方式,下面驗證下是否ok
1. HttpServletRequest方式擷取參數
測試case,可以借助curl來實作post請求
# case1
curl -d "user=小灰灰Blog&password=123456" "http://127.0.0.1:8080/webs/demo/req1"
## 輸出: req1 user: 小灰灰Blog pwd: 123456
# case2
curl -d "user=小灰灰Blog" "http://127.0.0.1:8080/webs/demo/req1?password=123456"
## 輸出:req1 user: 小灰灰Blog pwd: 12345
# case3
curl -d "user=小灰灰Blog" "http://127.0.0.1:8080/webs/demo/req1?user=greyBlog"
## 輸出:req1 user: greyBlog pwd: null
curl也可以換成js請求測試方式
var formData = new FormData();
formData.append("user", "小灰灰Blog");
$.ajax({
url: 'http://127.0.0.1:8080/webs/demo/req1?password=123456',
type: 'post',
cache: false,
data: formData,
processData: false,
contentType: false
});
說明
- 對于HttpServletReuqest方式擷取參數時,get和post沒什麼差別
- 若url參數和表單參數同名了,測試結果顯示使用的是url參數(待确認,當然最好不要這麼幹)
2. 方法參數擷取
幾個測試demo如下
# case 1
curl -d "user=小灰灰Blog&password=123456" "http://127.0.0.1:8080/webs/demo/req2"
## 輸出: req2 user: 小灰灰Blog pwd: 123456
# case 2
curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req2"
## 輸出:req2 user: null pwd: 123456
# case 3
curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req2?user=blog"
## 輸出: req2 user: blog pwd: 123456
基本上使用姿勢和get沒什麼差別
3. RequestParam注解方式
# case 1
curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req3"
## 輸出: req3 user: blog pwd: 123456
# case 2
curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req3?user=blog"
## 輸出: req3 user: blog pwd: 123456
# case 3
curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req3?password=900"
## 輸出:req3 user: blog pwd: 900,123456
注意
- 和前面的兩種方式不同的是,當post表單的參數和url參數同名時,會合并成一個字元串
4. Bean方式
## case1
curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req4?password=900"
## 輸出 req4 userDO: DemoController.UserDO(user=blog, password=900,123456)
## case2
curl -d "password=123456&user=blog" "http://127.0.0.1:8080/webs/demo/req4"
## 輸出 req4 userDO: DemoController.UserDO(user=blog, password=123456)
## case3
curl -d "password=123456" "http://127.0.0.1:8080/webs/demo/req4"
## 輸出 req4 userDO: DemoController.UserDO(user=null, password=123456)
這種方式不區分get,post,是以完全複雜的互動接口,完全可以考慮用bean的方式來定義請求參數
5. PathVariable
這個沒法玩…
III. 多媒體上傳參數擷取
上傳檔案的支援,對于傳統的spring-mvc來說,可能需要一些添加一些相關配置,不在本文的範疇内,下面預設已經配置好
1. 執行個體支援
@RequestMapping(path = {"wx/upload", "wx/wx/upload"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
@ResponseBody
public String upload(HttpServletRequest request) {
MultipartFile file = null;
if (request instanceof MultipartHttpServletRequest) {
file = ((MultipartHttpServletRequest) request).getFile("image");
}
if (file == null) {
throw new IllegalArgumentException("圖檔不能為空!");
}
return "success";
}
簡單來說,主要是利用
HttpServletRequest
來擷取上傳的檔案
注意:
- 如果接口必須要求上傳檔案,可以直接把參數聲明為
, 此時調用方如果不傳參數,會被異常攔截(可以通過@ControllerAdvice來攔截全局異常)MultipartHttpServletRequest
- 如果可以不上傳檔案,則可以用上面的這種猥瑣姿勢,内部進行判斷
-
來擷取指定名的上傳檔案((MultipartHttpServletRequest) request).getFile(xxx)
IV. 小結
1. 五種擷取參數的姿勢
方式 | 注意事項 |
HttpServletRequest擷取參數 | 最常見通用 |
方法參數與請求參數同名 | 注意參數名統一,注意類型一緻,盡量不用非包裝基本類型 |
@RequestParam注解 | 同上,可注解内指定http參數名 |
Bean方式 | 定義一個bean,會将同名的http參數指派進去,推薦 |
@PathVariable 注解 | 請求url參數 |
2. 傳檔案使用姿勢
使用
MultipartHttpServletRequest
來擷取上傳的檔案,當然也可以擷取基本的請求參數