@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。
@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" ; } }
@PostMapping(value = "/header") public String header(@RequestHeader(name = "Content-Type") String contentType) { return contentType; }
拓展底層:1>引數處理器是RequestHeaderMethodArgumentResolver,需要在註解中指定請求頭的Key。
@PostMapping(value = "/cookie") public String cookie(@CookieValue(name = "JSESSIONID") String sessionId) { return sessionId;}
拓展底層:1>引數處理器為ServletCookieValueMethodArgumentResolver,需要在註解中指定Cookie的Key。
@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中帶到頁面中進行渲染。
@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。
@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。
注意區別使用了上面提到的部分註解的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結構。
@PostMapping(value = "/user-2") public User saveUser2(@RequestBody User user) { log.info(user.toString()); return user; }
拓展底層:1>使用了@RequestBody註解,最終使用到的引數處理器為RequestResponseBodyMethodProcessor,實際上會用到
MappingJackson2HttpMessageConverter進行引數型別的轉換,底層依賴到Jackson相關的包。
@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一定是使用此引數處理器)。
getFiles
方法獲取MultipartFile列表@PostMapping(value = "/parts") public String partArgs(@RequestParam(name = "file") List<MultipartFile> parts) { log.info("{}", parts); return parts.toString(); }