拙見–springMVC的controller接受的請求參數

  • 2022 年 6 月 16 日
  • 筆記

 

 1-這種是最常用的表單參數提交,ContentType指定為application/x-www-form-urlencoded,也就是會進行URL編碼。

1.1-對象類型實體Bean接收請求參數(表單實體也可以用@ModelAttribute(“UserForm”)
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public String login(UserForm userForm, HttpSession session, Model model){
        if("wangguodong".equals(userForm.getUname()) && "123".equals(userForm.getUpass())){ //用戶名和密碼都相等
            session.setAttribute("u", userForm);
            return "main" ; //登錄成功,跳到主頁面
        }else{
            model.addAttribute("messageError", "用戶名或密碼錯誤") ;
            return "login" ;
        }
    }
拓展底層:1>因為沒有使用註解,最終的參數處理器為ServletModelAttributeMethodProcessor,主要是把HttpServletRequest中的表單參數封裝到MutablePropertyValues實例中,
再通過參數類型實例化(通過構造反射創建UserForm實例),反射匹配屬性進行值的填充
2>實際上RequestParamMethodArgumentResolver依賴WebConversionService中Converter列表進行參數轉換:因為沒有UserForm到String的轉換器,添加一個
org.springframework.core.convert.converter.Converter實現即可   

  1.2-非對象類型單個參數接收:通過處理方法的形參接收請求參數(就是直接把表單參數寫在控制類相應方法的形參中,形參名稱與請求參數名稱可以不一致時用@RequestParam(name = "name"))
@PostMapping(value = "/post")

public String post(@RequestParam(name = "name") String name, @RequestParam(name = "age") Integer age) { String content = String.format("name = %s,age = %d", name, age); log.info(content); return content; }
拓展底層:1>這種情況下,用到的參數處理器是RequestParamMapMethodArgumentResolver。

2-其他參數主要包括請求頭、Cookie、Model、Map等相關參數,還有一些並不是很常用或者一些相對原生的屬性值獲取;例如HttpServletRequest、HttpServletResponse等)

  2.1HttpServletRequest
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public String login(HttpServletRequest request, Model model){
        String uname = request.getParameter("uname") ;
        String upass = request.getParameter("upass") ;
        if("wangguodong".equals(uname) && "123".equals(upass)){ //用戶名和密碼都相等
            return "main" ; //登錄成功,跳到主頁面
        }else{
            model.addAttribute("messageError", "用戶名或密碼錯誤") ;
            return "login" ;
        }
    }
  2.2請求頭的值主要通過@RequestHeader註解的參數獲取,
@PostMapping(value = "/header")
public String header(@RequestHeader(name = "Content-Type") String contentType) {
   return contentType;
}
拓展底層:1>參數處理器是RequestHeaderMethodArgumentResolver,需要在註解中指定請求頭的Key。
  2.3Cookie的值主要通過@CookieValue註解的參數獲取,
@PostMapping(value = "/cookie")
public String cookie(@CookieValue(name = "JSESSIONID") String sessionId) {
    return sessionId;}
拓展底層:1>參數處理器為ServletCookieValueMethodArgumentResolver,需要在註解中指定Cookie的Key。
  2.4Model類型參數
@GetMapping(value = "/model")
public String model(Model model, ModelMap modelMap) {
    log.info("{}", model == modelMap);
    return "success";
}
拓展底層:1>Model類型參數的處理器是ModelMethodProcessor,實際上處理此參數是直接返回ModelAndViewContainer實例中的Model(ModelMap類型),因為要橋接不
同的介面和類的功能,因此回調的實例是BindingAwareModelMap類型,此類型繼承自ModelMap同時實現了Model介面。ModelMap或者Model中添加的屬性項會附加到
HttpRequestServlet中帶到頁面中進行渲染。
  2.5Errors或者BindingResult參數
@PostMapping(value = "/errors")
public String errors(@RequestBody @Validated ErrorsModel errors, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        for (ObjectError objectError : bindingResult.getAllErrors()) {
            log.warn("name={},message={}", objectError.getObjectName(), objectError.getDefaultMessage());
        }
    }
    return errors.toString();}//ErrorsModel@Data@NoArgsConstructorpublic class ErrorsModel {
    @NotNull(message = "id must not be null!")
    private Integer id;
    @NotEmpty(message = "errors name must not be empty!")
    private String name;
}

拓展底層:1>Errors其實是BindingResult的父介面,BindingResult主要用於回調JSR參數校驗異常的屬性項,如果JSR校驗異常,一般會拋出
MethodArgumentNotValidException異常,並且會返回400(Bad Request),見全局異常處理器DefaultHandlerExceptionResolver。Errors類型
的參數處理器為ErrorsMethodArgumentResolver。
  2.6Value參數
@GetMapping(value = "/value")
public String value(@Value(value = "${spring.application.name}") String name) {
    log.info("spring.application.name={}", name);
    return name;
}
拓展底層:1>控制器方法的參數可以是@Value註解修飾的參數,會從Environment中裝配和轉換屬性值到對應的參數中(也就是參數的來源並不是請求體),
參數處理器為ExpressionValueMethodArgumentResolver。
 2.7Map類型參數的範圍相對比較廣,對應一系列的參數處理器,

注意區別使用了上面提到的部分註解的Map類型和完全不使用註解的Map類型參數,兩者的處理方式不相同。下面列舉幾個相對典型的Map類型參數處理例子。

不使用任何註解的Map<String,Object>參數
這種情況下參數實際上直接回調ModelAndViewContainer中的ModelMap實例,參數處理器為MapMethodProcessor,往Map參數中添加的屬性將會帶到頁面中。

使用@RequestParam註解的Map<String,Object>參數
這種情況下的參數處理器為RequestParamMapMethodArgumentResolver,使用的請求方式需要指定ContentType為x-www-form-urlencoded,不能使用application/json的

@PostMapping(value = "/map")
public String mapArgs(@RequestParam Map<String, Object> map) {
    log.info("{}", map);
    return map.toString();
}

使用@RequestHeader註解的Map<String,Object>參數
這種情況下的參數處理器為RequestHeaderMapMethodArgumentResolver,作用是獲取請求的所有請求頭的Key-Value。

使用@PathVariable註解的Map<String,Object>參數
這種情況下的參數處理器為PathVariableMapMethodArgumentResolver,作用是獲取所有路徑參數封裝為Key-Value結構。

 

3-直接POST一個JSON字元串這種方式對於SpringMVC來說是比較友好的,只需要把ContentType設置為application/json,提交一個原始的JSON字元串即可:

@PostMapping(value = "/user-2")
public User saveUser2(@RequestBody User user) {
    log.info(user.toString());
    return user;
}
拓展底層:1>使用了@RequestBody註解,最終使用到的參數處理器為RequestResponseBodyMethodProcessor,實際上會用到
MappingJackson2HttpMessageConverter進行參數類型的轉換,底層依賴到Jackson相關的包。

4-帶請求路徑參數(例如/user/{userId}是一個URL模板(URL模板中的參數佔位符是{}),實際請求的URL為/user/1,那麼通過匹配實際請求的URL和URL模板就能提取到userId為1)

@GetMapping(value = "/user/{name}/{age}")
public String findUser1(@PathVariable(value = "age") Integer age, @PathVariable(value = "name") String name) {
    String content = String.format("name = %s,age = %d", name, age);
    log.info(content);
    return content;
}
注意一點是,@PathVariable的解析是按照value(name)屬性進行匹配,和URL參數的順序是無關的
其實路徑參數支援正則表達式,例如我們在使用/sex/sex}介面的時候,要求sex必須是F(Female)或者M(Male)
@GetMapping(value = "/sex/{sex:M|F}")
public String findUser2(@PathVariable(value = "sex") String sex){
    log.info(sex);
    return sex;
}
拓展底層:1>對應的參數處理器為PathVariableMethodArgumentResolver。

5-文件上傳在使用POSTMAN模擬請求的時候需要選擇form-data,POST方式進行提交:
@PostMapping(value = "/file1")
public String file1(@RequestPart(name = "file1") MultipartFile multipartFile) {
    String content = String.format("name = %s,originName = %s,size = %d",
            multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());
    log.info(content);
    return content;
}
拓展底層:1>可知MultipartFile實例的主要屬性分別來自Content-Disposition、content-type和content-length,另外,InputStream用於讀取請求體的最後部分
(文件的位元組序列)。參數處理器用到的是RequestPartMethodArgumentResolver(記住一點,使用了@RequestPart和MultipartFile一定是使用此參數處理器)。

6-批量文件上傳,我們一般需要接收一個MultipartFile集合,使用MultipartHttpServletRequest參數,直接調用getFiles方法獲取MultipartFile列表

@PostMapping(value = "/parts")
public String partArgs(@RequestParam(name = "file") List<MultipartFile> parts) {
    log.info("{}", parts);
    return parts.toString();
}