2.5万字长文简单总结SpringMVC请求参数接收 (5)

在一个控制器(使用了@Controller的Spring组件)中,如果存在一到多个使用了@ModelAttribute的方法,这些方法总是在进入控制器方法之前执行,并且执行顺序是由加载顺序决定的(具体的顺序是带参数的优先,并且按照方法首字母升序排序),举个例子:

@Slf4j @RestController public class ModelAttributeController { @ModelAttribute public void before(Model model) { log.info("before.........."); model.addAttribute("before", "beforeValue"); } @ModelAttribute(value = "beforeArg") public String beforeArg() { log.info("beforeArg.........."); return "beforeArgValue"; } @GetMapping(value = "/modelAttribute") public String modelAttribute(Model model, @ModelAttribute(value = "beforeArg") String beforeArg) { log.info("modelAttribute.........."); log.info("beforeArg..........{}", beforeArg); log.info("{}", model); return "success"; } @ModelAttribute public void after(Model model) { log.info("after.........."); model.addAttribute("after", "afterValue"); } @ModelAttribute(value = "afterArg") public String afterArg() { log.info("afterArg.........."); return "afterArgValue"; } }

调用此接口,控制台输出日志如下:

after.......... before.......... afterArg.......... beforeArg.......... modelAttribute.......... beforeArg..........beforeArgValue {after=afterValue, before=beforeValue, afterArg=afterArgValue, beforeArg=beforeArgValue}

可以印证排序规则和参数设置、获取的结果和前面的分析是一致的。

Errors或者BindingResult参数

Errors其实是BindingResult的父接口,BindingResult主要用于回调JSR参数校验异常的属性项,如果JSR303校验异常,一般会抛出MethodArgumentNotValidException异常,并且会返回400(Bad Request),见全局异常处理器DefaultHandlerExceptionResolver。Errors类型的参数处理器为ErrorsMethodArgumentResolver。举个例子:

@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 @NoArgsConstructor public class ErrorsModel { @NotNull(message = "id must not be null!") private Integer id; @NotEmpty(message = "errors name must not be empty!") private String name; }

调用接口控制台Warn日志如下:

name=errors,message=errors name must not be empty!

一般情况下,不建议用这种方式处理JSR校验异常的属性项,因为会涉及到大量的重复的硬编码工作,建议:方式一直接继承ResponseEntityExceptionHandler覆盖对应的方法或者方式二同时使用@ExceptionHandler和@(Rest)ControllerAdvice注解进行异常处理。例如:

@RestControllerAdvice public class ApplicationRestControllerAdvice{ @ExceptionHandler(BusinessException.class) public Response handleBusinessException(BusinessException e, HttpServletRequest request){ // 这里处理异常和返回值 } @ExceptionHandler(MethodArgumentNotValidException.class) public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request){ // 这里处理异常和返回值 } }

值得注意的是,SpringBoot某个版本之后,把JSR303相关的依赖抽离到spring-boot-starter-validation依赖中,如果要使用JSR303相关相关校验功能,必须独立引入此starter

@Value参数

控制器方法的参数可以是@Value注解修饰的参数,会从Environment实例中装配和转换属性值到对应的参数中(也就是参数的来源并不是请求体,而是上下文中已经加载和处理完成的环境属性值),参数处理器为ExpressionValueMethodArgumentResolver。举个例子:

@GetMapping(value = "/value") public String value(@Value(value = "${spring.application.name}") String name) { log.info("spring.application.name={}", name); return name; }

spring.application.name属性一般在配置文件中指定,在加载配置文件属性的时候添加到全局的Environment中。

Map类型参数

Map类型参数的范围相对比较广,对应一系列的参数处理器,注意区别使用了上面提到的部分注解的Map类型和完全不使用注解的Map类型参数,两者的处理方式不相同。下面列举几个相对典型的Map类型参数处理例子。

不使用任何注解的Map<String,Object>参数

这种情况下参数实际上直接回调ModelAndViewContainer中的ModelMap实例,参数处理器为MapMethodProcessor,往Map参数中添加的属性将会带到页面中。

使用@RequestParam注解的Map<String,Object>参数

这种情况下的参数处理器为RequestParamMapMethodArgumentResolver,使用的请求方式需要指定Content-Type为x-www-form-urlencoded,不能使用application/json的方式:

spmvc-p-10

控制器代码为:

@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结构。

MultipartFile集合-批量文件上传

批量文件上传的时候,我们一般需要接收一个MultipartFile集合,可以有两种选择:

使用MultipartHttpServletRequest参数,直接调用getFiles方法获取MultipartFile列表。

使用@RequestParam注解修饰MultipartFile列表,参数处理器是RequestParamMethodArgumentResolver,其实就是第1种方式的封装而已。

spmvc-p-11

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywxzx.html