天天看點

SpringMVC之請求參數的擷取方式

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​

​來擷取上傳的檔案

注意:

  • 如果接口必須要求上傳檔案,可以直接把參數聲明為 ​

    ​MultipartHttpServletRequest​

    ​, 此時調用方如果不傳參數,會被異常攔截(可以通過@ControllerAdvice來攔截全局異常)
  • 如果可以不上傳檔案,則可以用上面的這種猥瑣姿勢,内部進行判斷
  • ​((MultipartHttpServletRequest) request).getFile(xxx)​

    ​來擷取指定名的上傳檔案

IV. 小結

1. 五種擷取參數的姿勢

方式 注意事項
HttpServletRequest擷取參數 最常見通用
方法參數與請求參數同名 注意參數名統一,注意類型一緻,盡量不用非包裝基本類型
@RequestParam注解 同上,可注解内指定http參數名
Bean方式 定義一個bean,會将同名的http參數指派進去,推薦
@PathVariable 注解 請求url參數

2. 傳檔案使用姿勢

使用​

​MultipartHttpServletRequest​

​來擷取上傳的檔案,當然也可以擷取基本的請求參數

V. 其他

聲明

掃描關注,java分享

繼續閱讀