Spring Boot 对参数进行校验
在后台对前端提交的参数验证是必须的。Spring Boot 内置了相关参数校验。
1、添加依赖
使用的是Spring Boot 2.7版本,经过测试需要安装 spring-boot-starter-validation 依赖。如果 pom 没有从 parent 继承,需要添加版本号。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
2、 @RequestParam注解参数校验
前后端参数提交的方式很多,一般使用 @RequestParam ,@RequestBody注解自动获取参数,有关这两者的区别参考:SpringBoot中使用注解@RequestParam与 @RequestBody区别
对@RequestParam的参数校验如下:
controller上添加 @Validated 注解,参数前添加对应注解。
@Validated @RestController @RequestMapping("/load") public class PayloadController { @PostMapping("manage") public ResponseWrapper postEarthStation( @RequestParam("id") @Min(0) @Max(127) int id, ...... ) { ......return reply.equals("success") ? ResponseWrapper.success : ResponseWrapper.fail; } }
3、@RequestBody注解参数校验
@RequestBody 一般对 content-type: application/json 类型的参数进行校验,需要定义一个实体类,字段和前端参数需要一一对应。
定义实体类,并对实体类字段进行校验。
public class TmParamEntity { @NotNull(message = "satId字段不能为空") private int satId; ...... }
需要添加两个注解: controller加上@Validated注解,参数前添加@Valid注解。
@Validated @RestController @RequestMapping("tele") public class TelemetryController { @PostMapping("postData") public ResponseWrapper postTmData(@RequestBody @Valid List<TmParamEntity> tmParamEntity, HttpServletRequest httpServletRequest) { ...... return ResponseWrapper.success; } }
需要注意的是,如果前端传递参数是一个对象,那么参数类型就是实体类型,如果前端传递的是一个数组Array,这里需要使用 List<TmParamEntity> 类型。添加 @Valid注解,即可使参数校验生效。参数的反序列化工作,Spring 内置了Jackson 自动处理。如果字段没对上,可能会报错。具体报错分为一下几种情况:
1、数据结构没对上:
例如一级数据结构,应该传对象{},实际传入Array [],这种情况会直接报错,前端会默认收到400 Bad Request。
2、对象少了字段:
这种情况不会报错,因为定义的实体类字段都有默认值,int 是 0, double是 0.0,引用类型为null。未传入的字段 Jackson会使用默认值。
3、对象多了字段:
不会报错,多出的字段会直接忽略。
4、二级结构数据结构没对上:
例如:{ "data": [] } 如果传入 { "data" : 1 } 由于类型无法对上,会直接报错。
总结一下:Jackson反序列化 只要数据结构统一便不会报错,缺失的字段会使用实体类的默认值,多余的字段会被忽略。
4、直接读取InputStream
有时我们需要无需借助 Jackson反序列化,直接从 httpServletRequest 拿到参数,有两种方法:
InputStreamReader inputStreamReader = new InputStreamReader(httpServletRequest.getInputStream()); char[] chars = new char[1024]; StringBuilder stringBuilder = new StringBuilder(); while ((inputStreamReader.read(chars)) != -1) { stringBuilder.append(new String(chars).trim()); }
StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = httpServletRequest.getReader()) { String lineString = bufferedReader.readLine(); while (lineString != null) { stringBuilder.append(lineString); lineString = bufferedReader.readLine(); } }
前者从 InputStreamReader 中读,后者直接从 BufferedReader 中读数据。需要注意的是,无论是哪种读取方式,stream数据只能读一次!例如使用了RequestBody 自动解析参数,再用 bufferedReader 去读,返回的是个null。另外,这里使用了try(resource) 对网络资源进行释放。
5、Jackson序列化与反序列化
需求随时会变,如果不借助 @RequestBody 自动对参数进行反序列化, 那就需要手动解析。利用Jackson手动解析参数如下:
ObjectMapper objectMapper = new ObjectMapper(); OrbitDataEntity orbitDataEntity = objectMapper.readValue(response.body(), OrbitDataEntity.class); //反序列化 String transitInfoEntityListJson = objectMapper.writeValueAsString(orbitDataEntity.getData()); // 序列化
注意的是, response.body()返回的字符串中包含的字段和数据结构,需要和第二个参数OrbitDataEntity 实体类字段和数据结构一一对应。这一点和上述利用@RequestBody 自动反序列化是相同的。
6、全局异常捕获
添加全局异常捕获类GlobalExceptionHandler ,代码如下。并统一返回对应的数据结构ResponseWrapper 。
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = MethodArgumentNotValidException.class) public ResponseWrapper handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { return ResponseWrapper.fail.setData(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); } @ExceptionHandler(value = HttpMessageNotReadableException.class) public ResponseWrapper handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { return ResponseWrapper.fail.setData(e.getMessage()); } }
参考链接: https://www.w3cschool.cn/article/53012322.html