Spring Validation校验框架@Validated注解常规校验,分组校验的使用,以及搭配全局异常处理器一起使用。

注:Sring Validation验证框架对参数的校验机制提供了@Validated(Spring JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。

    @Validated配合BindingResult使用个人觉得比较死板,如果不做处理测试校验结果只能校验出有数据不符合制定的规则,并不能校验哪个数据出现了异常。而前端需要拿到你返回的异常信息,所以不能在不作处理的情况下直接给他,不然前端会砍你的。所以后面会提到搭配全局异常处理器使用。

注解说明

空校验	针对类型	说明
@Null	任何类型	校验对象必须为空
@NotNull	任何类型	校验对象不为空,不能校验字符串长度为0的对象
@NotBlank	字符串	只对字符串有效,校验字符串去掉前后空格后长度不为0
@NotEmpty	字符串、集合、数组	校验对象不能为空 (字符串长度不为0、集合大小不为0)


boolean校验	针对类型	说明
@AssertTrue	布尔	校验boolean类型必须为true
@AssertFalse	布尔	校验boolean类型必须为false


数值校验	针对类型	说明
@Min	数字类型	校验必须是一个数字,其值必须大于或等于指定的最小值
@Max	数字类型	校验必须是一个数字,其值必须小于或等于指定的最大值
@DecimalMin	数字类型	校验必须是一个数字,其值必须大于或等于指定的最小值
@DecimalMax	数字类型	校验必须是一个数字,其值必须小于或等于指定的最大值
@Digits(integer=,fraction=)	数字类型	校验必须是一个数字,interger指定整数精度,fraction指定小数精度
@Range(min =,max = )	数字类型、字符串	校验对象的值在min和max区间内
@Length(min =,max = )	字符串	校验对象的值的长度在min和max区间内
@Size(min =,max = )	字符串、集合、数组	校验对象的值在min和max区间内,如字符长度、集合大小


其他校验	针对类型	说明
@Email	字符串	校验对象的值必须是Email类型,也可以通过regexp和flag指定自定义的email格式
@Pattern	字符串	校验对象的值必须符合指定的正则表达式
@CreditCardNumber	数字类型、字符串	校验对象的值必须是信用卡类型
@URL	字符串	校验对象的值必须是URL地址

springboot项目已经默认导入了基础包,所以无需另行导包,直接使用即可。

1.首先安排一个实体类,下面是在工作时写功能测试用的,今天就用它。

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

/**
 * 文件压缩记录表
 * 
 * @author XRJ
 * @email [email protected]
 * @date 2020-04-06 20:46:31
 */
@Data
@Valid
@TableName("hostconfiguration")
public class HostconfigurationEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 主键id
	 */
	@TableId
	@NotNull(groups = {Update.class})
	private int id;

	/**
	 * 服务器ip
	 */
	@NotNull(groups = {Insert.class})
	@Pattern(groups = {Insert.class, Update.class, Select.class}, regexp = "((2[0-4]\\d|25[0-5]|1?[1-9]?\\d)\\.){3}(2[0-4]\\d|25[0-5]|1?[1-9]?\\d)", message = "请输入合法的IP地址")
	private String localhostIp;

	/**
	 * 服务器账号
	 */
	@NotBlank(groups = {Insert.class})
	@Pattern(groups = {Insert.class, Update.class}, regexp = "\\w{1,32}", message = "用户名只能[a-zA-Z0-9_]中字符,长度不能超过32位")
	private String localhostUsername;

	/**
	 * 服务器密码
	 */
	@NotBlank(groups = {Insert.class})
	@Length(groups = {Insert.class, Update.class}, min = 1, max = 32)
	private String localhostPassword;

	/**
	 * 端口号
	 */
	@NotNull(groups = {Insert.class})
	private Integer localhostPort;

	/**
	 * 需压缩文件路径
	 */
	@NotBlank(groups = {Insert.class})
	private String localhostDirectory;

	/**
	 * 天数,压缩几天前的日志文件
	 */
	@NotNull(groups = {Insert.class})
	private Integer numTime;

	/**
	 * 创建时间
	 */
	private Date createTime;
	/**
	 * 是否成功,0失败,1成功
	 */
	private Integer isSuccess;

}

上边groups = {Insert.class,Update.class,Select.class,Delete.class}属于分组校验,分别对应增删改查。在controller层使用@Validated(Insert.class,Update.class,Select.class,Delete.class),则会启用相应校验。

2.controller层代码如下

package cn.cm.filecompress.controller;

import cn.cm.filecompress.entity.HostconfigurationEntity;
import cn.cm.filecompress.service.HostconfigurationService;
import cn.cm.filecompress.tool.FileCompressTool;
import cn.cm.filecompress.tool.Result;
import com.jcraft.jsch.JSchException;
import org.apache.ibatis.annotations.Insert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 文件压缩记录表
 *
 * @author XRJ
 * @email [email protected]
 * @date 2020-04-06 20:46:31
 */
@RestController
@RequestMapping("/hostconfiguration")
public class HostconfigurationController {
    @Autowired
    private HostconfigurationService hostconfigurationService;

    @PostMapping(value = "/add",produces = "application/json;charset=UTF-8")
    public Result<Integer> insert(@RequestBody @Validated(Insert.class) HostconfigurationEntity hostconf){
        return hostconfigurationService.insert(hostconf);
    }
}

目前还没有配合全局异常捕获器,先用postman测试一下,下面是我模拟的数据

{
	"localhostPort":22,
	"localhostPassword":"",
	"localhostUsername":"",
	"localhostDirectory":"",
	"localhostIp":"192.156.124.90",
	"numTime":3
}

这是返回的结果,很明显,把整个异常信息带回来了,不知道是哪个属性异常了,这时候全局异常处理器来了:

{"timestamp":1587890386937,"status":400,"error":"Bad Request","errors":[{"codes":["Length.hostconfigurationEntity.localhostPassword","Length.localhostPassword","Length.java.lang.String","Length"],"arguments":[{"codes":["hostconfigurationEntity.localhostPassword","localhostPassword"],"arguments":null,"defaultMessage":"localhostPassword","code":"localhostPassword"},32,1],"defaultMessage":"长度需要在1和32之间","objectName":"hostconfigurationEntity","field":"localhostPassword","rejectedValue":"","bindingFailure":false,"code":"Length"},{"codes":["NotBlank.hostconfigurationEntity.localhostPassword","NotBlank.localhostPassword","NotBlank.java.lang.String","NotBlank"],"arguments":[{"codes":["hostconfigurationEntity.localhostPassword","localhostPassword"],"arguments":null,"defaultMessage":"localhostPassword","code":"localhostPassword"}],"defaultMessage":"不能为空","objectName":"hostconfigurationEntity","field":"localhostPassword","rejectedValue":"","bindingFailure":false,"code":"NotBlank"},{"codes":["Pattern.hostconfigurationEntity.localhostUsername","Pattern.localhostUsername","Pattern.java.lang.String","Pattern"],"arguments":[{"codes":["hostconfigurationEntity.localhostUsername","localhostUsername"],"arguments":null,"defaultMessage":"localhostUsername","code":"localhostUsername"},[],{"defaultMessage":"\\w{1,32}","arguments":null,"codes":["\\w{1,32}"]}],"defaultMessage":"用户名只能[a-zA-Z0-9_]中字符,长度不能超过32位","objectName":"hostconfigurationEntity","field":"localhostUsername","rejectedValue":"","bindingFailure":false,"code":"Pattern"},{"codes":["NotBlank.hostconfigurationEntity.localhostUsername","NotBlank.localhostUsername","NotBlank.java.lang.String","NotBlank"],"arguments":[{"codes":["hostconfigurationEntity.localhostUsername","localhostUsername"],"arguments":null,"defaultMessage":"localhostUsername","code":"localhostUsername"}],"defaultMessage":"不能为空","objectName":"hostconfigurationEntity","field":"localhostUsername","rejectedValue":"","bindingFailure":false,"code":"NotBlank"},{"codes":["NotBlank.hostconfigurationEntity.localhostDirectory","NotBlank.localhostDirectory","NotBlank.java.lang.String","NotBlank"],"arguments":[{"codes":["hostconfigurationEntity.localhostDirectory","localhostDirectory"],"arguments":null,"defaultMessage":"localhostDirectory","code":"localhostDirectory"}],"defaultMessage":"不能为空","objectName":"hostconfigurationEntity","field":"localhostDirectory","rejectedValue":"","bindingFailure":false,"code":"NotBlank"}],"message":"Validation failed for object='hostconfigurationEntity'. Error count: 5","path":"/compress/hostconfiguration/add"}

3.Validation校验框架配合全局异常处理器,全局异常处理器代码如下:

package cn.cm.filecompress.tool;

import com.alibaba.fastjson.JSONObject;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * description : 全局异常处理器
 */
@ControllerAdvice
public class GlobalExceptionHandler {

//    private static final SystemLogger LOGGER = SystemLogger.getLogger(GlobalExceptionHandler.class);

    /**
     * 方法入参支持:Exception、SessionAttribute、@RequestAttribute、HttpServletRequest、HttpServletResponse、HttpSession
     * 方法返回支持: ModelAndView、@ResponseBody、ResponseEntity
     */
    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public Result<String> error(HttpServletResponse response, Exception e) {
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
//        LOGGER.error("全局异常捕获", e);
        try {
            response.getWriter().print(JSONObject.toJSONString(Result.of(Code.SC_INTERNAL_SERVER_ERROR.getState(), Code.SC_INTERNAL_SERVER_ERROR.getDescription(), e.toString())));
        } catch (IOException exc) {
//            LOGGER.error("全局异常捕获响应失败", exc);
        }
        return null;
//        return new Result<>(Code.SC_INTERNAL_SERVER_ERROR, e.toString());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public String handleMethodArgumentNotValid(HttpServletResponse response, Exception e) {
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
        BindingResult bindingResult = ex.getBindingResult();
        Map<String, Object> map = new HashMap<>();
        for (FieldError error : bindingResult.getFieldErrors()) {
            String field = error.getField();
            String msg = error.getDefaultMessage();
//            Object value = error.getRejectedValue();
//            map.put(field, String.format("{'value':'%s','errorMsg','%s'}", value, msg));
            map.put(field, msg);
        }
        try {
            response.getWriter().print(JSONObject.toJSONString(Result.of(Code.SC_PRECONDITION_FAILED.getState(), Code.SC_PRECONDITION_FAILED.getDescription(), map)));
        } catch (IOException exc) {
//            LOGGER.error("统一参数异常处理响应失败", exc);
        }
        return null;
//        return new Result<>(Code.SC_PRECONDITION_FAILED, map);
    }
}

使用之前的数据再一次请求接口,返回数据如下:

{
    "code": 412,
    "data": {
        "localhostPassword": "长度需要在1和32之间",
        "localhostUsername": "用户名只能[a-zA-Z0-9_]中字符,长度不能超过32位",
        "localhostDirectory": "不能为空"
    },
    "message": "客户端请求信息的先决条件错误"
}

如此一来,可以很清楚的看到,请求体中是哪个属性出现了异常,这就是全局异常处理器,可以好好研究一下,由于公司的编码要求严格,数据返回格式做了统一,大家可以自定义。

Sring Validation验证框架就到此结束,后面会逐个更新springcloud核心组件的配置与使用当做个人的云笔记本,觉得不错的小伙伴点个关注哈哈。。。

猜你喜欢

转载自blog.csdn.net/weixin_46792649/article/details/105768359