拙見--springMVC的controller接受的請求引數

_Origin 發表於 2022-06-16
Spring

 拙見--springMVC的controller接受的請求引數

 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();}//[email protected]@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();
}