第三节 SpringBoot参数校验

一、参数校验

          常见的用于校验的注解有

@NotNull      用于校验数字类型         javax.validation.constraints.NotNull;

@Max           用于校验数字最大值      javax.validation.constraints.Max;

@Min            用于校验数字最小值      javax.validation.constraints.Min;

@NotEmpty  用于校验集合长度         javax.validation.constraints.NotEmpty

@NotBlank   用于校验字符串非空      javax.validation.constraints.NotBlank

@Length       用于校验字符串长度     org.hibernate.validator.constraints.Length

          编写一个电话Phone实体,使用上述的注解。

package com.zhoutianyu.learnspringboot.valid;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.*;

@Data
public class Phone {

    @NotNull(message = "电话号码不能为空")
    private Long number;


    @Max(value = 5000, message = "预算价格不能超过{value}元")
    @Min(value = 1000, message = "预算价格不能低于{value}元")
    private double price;

    @NotBlank(message = "品牌不能为空")
    @Length(max = 5, message = "品牌长度不能超过{max}")
    private String brand;
}

        再编写一个PhoneController,使用上面的这个电话实体。

        只要在实体前面加上@Valid注解,并且在后面使用一个叫做BindingResult的实体来保存错误信息,就能实现过滤效果。

        下面的实现中,把错误的数据保存在了一个Map集合中。实际中会把map中的错误信息再传给前端小姐姐。

package com.zhoutianyu.learnspringboot.valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class PhoneController {
    private static final Logger LOGGER = LoggerFactory.getLogger(PhoneController.class);

    @GetMapping(value = "/phone/test")
    public Phone buyPhone(@Valid Phone phone, BindingResult result) {
        List<FieldError> fieldErrors = result.getFieldErrors();
        Map<String, Object> errorMap = new HashMap<>(10);
        fieldErrors.forEach(error -> {
                    LOGGER.error("字段:{}校验失败, Message:{} \n", error.getField(), error.getDefaultMessage());
                    errorMap.put(error.getField(), error.getDefaultMessage());
                }
        );
        return phone;
    }
}

二、测试

        启动项目,在浏览器上访问http://localhost:8081/study/springboot/phone/test

        控制台输出:

       

       加上一些参数看看。

       在浏览器上访问http://localhost:8081/study/springboot/phone/test?brand=HUAWEI&price=20000&number=110120119

       添加了三个参数。品牌为HUAWEI,但是超过了最大5个字的限制,价格传了2万,超过了预算。下图符合预期。

      

三、补充

       其实上述的第二个参数BindingResult可以忽略,那么错误信息将会以异常的形式抛出来。所以一般公司里的项目会使用一个全局异常拦截器来拦截此请求。

       下面是我的测试用例。

        有一个查询实体DictionQuery,它专门用于接收前端传过来的参数,dicTypeId不能为空。

public class DictionQuery ...

    @NotNull(message = "dicTypeId不能为空")
    private Long dicTypeId;
}

        在Controller层里面有一个GET请求方法,它没有使用BindingResult参数来接收校验结果。如果参数里面的dicTypeId为null,那么就会抛出异常BindingException

    @GetMapping("/dic/list")
    public MultiResponse<DicInfoVO> getDicList(@Valid DicListQry dicListQry) {
        return null;
    }

        如果我再把Get请求转成Post请求,dicTypeId的值还是传null。那么抛出的异常不一样,抛出 MethodArgumentNotValidException

    @PostMapping("/dic/list")
    public MultiResponse<DicInfoVO> getDicListPost(@RequestBody @Valid DicListQry dicList) {
        return null;
    }

        还有一种情况就是校验集合。为了支持校验集合的方式,不能够直接使用List来接收,不然不会走校验框架。为了校验集合,需要按照下面方式。把接收的List做成一个类的成员变量,然后在List上添加@Valid注解。 请求的时候,需要配合使用@Validated注解。参考:Java 对list对象进行属性校验

    @PostMapping("/dic/list")
    public MultiResponse<DicInfoVO> 
                         getDicListPost(@RequestBody @Validated DicList dicList) {
        return dictionaryService.getDicList(null);
    }


    @Data
    private static class DicList {

        @Valid
        private List<DicListQry> dicListQry;
    }

       一般用于处理全局响应的代码:

@ExceptionHandler({MethodArgumentNotValidException.class, 
                      BindException.class, ConstraintViolationException.class})
    public Response handlerViolationException(Exception exception) ...
        String errMsg = "服务器内部错误";
        if (exception instanceof MethodArgumentNotValidException) {
            errMsg = handlerBindingResult(((MethodArgumentNotValidException) exception).getBindingResult());
        } else if (exception instanceof BindException) {
            errMsg = handlerBindingResult(((BindException) exception).getBindingResult());
        } else if (exception instanceof ConstraintViolationException) {
            errMsg = handlerConstraintViolationException((ConstraintViolationException) exception);
        }

        // return 默认错误实体...
    }


private String handlerBindingResult(BindingResult bindingResult) {
        return bindingResult.getFieldErrors()
                .stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(", "));
    }

private String handlerConstraintViolationException(ConstraintViolationException exception) {
        // 使用Set去除集合中重复的错误信息
        Set<String> errMessages = exception.getConstraintViolations()
                .stream().map(ConstraintViolation::getMessage).collect(Collectors.toSet());
        return String.join(", ", errMessages);
    }

 

四、源码下载

        本章节项目源码:点我下载源代码

        目录贴:跟着大宇学SpringBoot-------目录帖

posted @ 2022-07-17 12:14  小大宇  阅读(293)  评论(0编辑  收藏  举报