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

posted @ 2022-08-25 09:56  恩恩先生  阅读(1305)  评论(0编辑  收藏  举报