Spring Boot参数验证

前言

参数验证是一个应用中必不可少的一部分操作,参数验证又可以分为前端验证和后端验证。如果没有参数验证的话,我们的逻辑可能就会报错,例如:空指针异常,更严重的可能造成系统的瘫痪。
参数验证又可以分为前端验证和后端验证,前端验证可以通过各种手段进行跳过,例如直接调用接口等等。为了防护系统出现各种异常,后端验证是不可取少的一部分。本文将主要描述使用Spring Boot项目的时候,后端如何进行参数验证。

一、参数传递的方式

  1. 请求路径中携带的参数,一般是GET请求
  2. 在请求体中携带的参数,一般是POST、PUT请求

二、参数验证注解

我们主要是使用javax.validation.constraintsorg.hibernate.validator.constraints包下的注解进行参数验证。常见的注解如下:

  • @NotNull:表示参数不能为Null,如果为空的话,就无法通过验证
  • @Max:一般用于数字,表示不能超过最大值

三、参数验证——请求路径中携带的参数

一般用于GET请求中。

例1:希望name不能为空,age不能大于100的限制条件。

@RestController
@Validated
public class TestController {
	@GetMapping("/testget")
    public JSONObject testget(@NotNull(message = "姓名不能为空") String name,
                              @Max(value = 100, message = "不能大于100") Integer age) {
        JSONObject jsonObject = new JSONObject();
        return jsonObject;
    }
}

如果我们访问localhost:8080/testget或者localhost:8080/testget?age=101或者localhost:8080/testget?age=1,就会抛出一个ConstraintViolationException的异常信息。前端就会收到一个500的错误信息。我们可以进行异常统一捕获,在后面进行描述,从而向前端返回统一的错误信息。

在使用路径中携带参数的注意事项:
  1. 必须在上添加@Validated注解
  2. 在需要验证的参数中添加验证注解。
  3. 使用路径中携带参数如果没有通过验证,就会抛出ConstraintViolationException的异常信息。

四、参数验证——请求体中携带的参数

一般用于POST、PUT请求

我们使用Restful的格式传递参数。参数使用JSON格式传递。
使用请求体中携带参数的方式主要有三种形式。

下面是使用的实体:包含一个嵌套实体的ChildDTO类

public class AreaDTO {

    @NotNull(groups = {UpdateValidation.class}, message = "区域ID不能为空")
    private Long id;

    @NotNull(message = "父级ID不能为空", groups = {AddValidation.class})
    private Long parentId;

    @NotNull(message = "片区名称不能为空")
    private String name;

	@Valid
    private ChildDTO childDTO;
}

public class ChildDTO {

    @NotNull(message = "不能为空")
    private String childName;

    @NotNull(groups = {AddValidation.class}, message = "不能为空")
    private Long childId;

	@Max(groups = {UpdateValidation.class}, value = 100 ,message = "不能为空")
    @Min(groups = {AddValidation.class}, value = 50, message = "最小值")
    private Long age;
}
//分组一
public interface AddValidation {
}
//分组二
public interface UpdateValidation {
}
方式1:抛出异常方式

例2:

	@PostMapping("/testpost")
    public JSONObject testpost(@Validated @RequestBody AreaDTO areaDTO) {
        JSONObject jsonObject = new JSONObject();
        return jsonObject;
    }

例3

	@PostMapping("/testpost1")
    public JSONObject testpost1(@Validated(value = {AddValidation.class}) @RequestBody AreaDTO areaDTO) {
        JSONObject jsonObject = new JSONObject();
        return jsonObject;
    }

上面两个例子中,第一个例子使用了默认的分组(即Default),而第二个例子使用了AddValidation的分组。我们在添加验证条件的时候,可以为其指定分组限制,使用groups属性,例如:@NotNull(message = "父级ID不能为空", groups = {AddValidation.class}),这个验证限制只有在@Validated(value = {AddValidation.calss})才会起作用。只有符合验证条件的分组才会进行验证。

例如:例2中,没有添加分组,默认使用Default分组,就会验证AreaDTO.nameAreaDTO.ChildDTO.childName两个属性。例3中,添加了AddValidation的验证分组,就会验证AreaDTO.parentIdAreaDTO.ChildDTO.childIdAreaDTO.ChildDTO.age三个属性。

当无法通过验证条件的时候,就会抛出一个MethodArgumentNotValidException的异常信息。里面包含了所有所有没有通过验证的属性。

注意:没有添加分组,则默认使用Default的分组。

在使用请求体中抛出异常的注意事项:
  1. 在需要验证参数前面添加@Validation注解,
  2. 在需要验证属性前面添加@Max等验证条件
  3. 如果验证无法通过,就会抛出一个MethodArgumentValidException的异常
方式二:使用实体封装方式

例4

	@PostMapping("/testpost4")
    public JSONObject testpost4(@Validated @RequestBody AreaDTO areaDTO, BindingResult result) {
    	//手动验证是否有没有通过的参数
    	if (result.hasErrors()) {
            for (FieldError fieldError : result.getFieldErrors()) {
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
            }
            //这里返回我们自定义的返回结果
        }
        JSONObject jsonObject = new JSONObject();
        return jsonObject;
    }
在使用请求体中实体封装的注意事项:
  1. 在需要验证的参数前面添加@Validated注解
  2. 在需要验证的参数后面,添加BindingResult的实体
  3. 在需要验证的属性前面,添加@Max等验证属性
  4. 没有通过验证,就会绑定到BindingResult实体中,不会抛出异常,必须手动进行判断
方式三:手动验证

例5:

	@Autowired
	private Validator validator;
	
	@PostMapping("/testpost5")
    public JSONObject testpost5(@RequestBody AreaDTO areaDTO) {
        Set<ConstraintViolation<AreaDTO>> validate = validator.validate(areaDTO, AddValidation.class, Default.class);
        if (!validate.isEmpty()) {
            String message = validate.iterator().next().getMessage();
            logger.error(message);
        }
        JSONObject jsonObject = new JSONObject();
        return jsonObject;
    }
在使用请求体中手动验证的注意事项:
  1. 在需要验证的属性前面,添加@Max等验证属性
  2. 使用Validator实例调用validate方法验证
  3. 可以在任何地方进行参数验证,更加灵活

到此,就完成了参数验证的全部内容。各种方式各有不同。

附录一:添加统一异常处理
@RestControllerAdvice
public class CommonRuntimeExceptionHandle {

	@ExceptionHandler(ConstraintViolationException.class)
    public JSONObject methodArgumentNotValidExceptionHandle(ConstraintViolationException methodArgumentNotValidException) {
        Set<ConstraintViolation<?>> constraintViolations = methodArgumentNotValidException.getConstraintViolations();
        String message = "";
        for (ConstraintViolation<?> next : constraintViolations) {
            message += next.getMessage()+" , ";
        }
        JSONObject responseResult = new JSONObject();
        responseResult.put("message", "参数不正确:" + message);
        responseResult.put("code", "-1");
        return responseResult;
    }
	
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JSONObject methodArgumentNotValidExceptionHandle(MethodArgumentNotValidException exception) {
        FieldError fieldError = exception.getBindingResult().getFieldError();
        MethodParameter parameter = exception.getParameter();
        logger.error("在执行{}.{}的时候出现绑定JSON数据参数异常,字段名称为:{},提示消息:{}",
                parameter.getDeclaringClass().getName(), parameter.getMethod().getName(), fieldError.getField(),
                fieldError.getDefaultMessage());
        JSONObject responseResult = new JSONObject();
        responseResult.put("message", "参数不正确:" + fieldError.getDefaultMessage());
        responseResult.put("code", "-1");
        return responseResult;
    }
}

我们使用@RestControllerAdvice的注解,标识这个类为控制器的增强类。

附录二:快速失败

默认情况下,Validator会验证所有需要验证的参数。如果有多个错误,就会出现多个字段异常信息。如果希望,当出现一个参数异常的时候,直接退出,不在继续验证其他参数的正确性。需要配置快速退出

@Configuration
public class ValidateConfig {

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure().failFast(true).buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值