• java web项目如何优雅的进行入参的校验


    前言

    之前看过有一个同事写的代码,他为了进行细粒度的返回值提示,针对每一种参数不合法的情况,都规定了一个code值,然后在service层去进行各种校验,捕捉各种异常,然后返回给controller不同的code码。controller再根据这些code码,返回不同的错误提示。

    他这样做可以改进的地方有两处:

    1. service层先根据错误类型返回不同的code码,controller再根据不同的code码返回不同的文案。进行了两步处理,代码量显得特别大。
    2. 在service层捕捉了大量异常,造成事务失效,为后人埋下了很多坑。

    javax.validation

    介绍

    javax.validation 是基于JSR-303标准开发出来的,使用注解方式实现,及其方便,但是这只是一个接口,没有具体实现。

    Hibernate-Validator是一个hibernate独立的包,可以直接引用,他实现了javax.validation同时有做了扩展,比较强大。

    SpringBoot在spring-boot-starter-web中引入了hibernate-validator,所以我们在springboot的web项目中可以直接使用。

    具体使用

    我就直接堆代码了, 由于代码比较多,所以用了一种折叠的方式,不过貌似只有chrome等少数的几个浏览器才支持。

    返回值的包装类:

    展开源码
    
    public class ResponseData implements Serializable {
    
        private String code;
    
        private String message;
    
        private T data;
    
        public String getCode() {
            return code;
        }
    
        public void setCode(String code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        public static class ResponseDataBuilder {
            private ResponseData responseData;
    
            public ResponseDataBuilder() {
                responseData = new ResponseData<>();
            }
    
            public ResponseDataBuilder code(String code) {
                responseData.setCode(code);
                return this;
            }
    
            public ResponseDataBuilder message(String message) {
                responseData.setMessage(message);
                return this;
            }
    
            public ResponseDataBuilder data(T data) {
                responseData.setData(data);
                return this;
            }
    
            public ResponseDataBuilder success() {
                responseData.setCode("0000");
                responseData.setMessage("请求成功");
                return this;
            }
    
            public ResponseData build() {
                return responseData;
            }
    
        }
    }
    
    

    请求实体类:
    展开源码
    
    public class Phone {
    
        @NotEmpty(message = "手机名称不能为空")
        private String name;
    
        private String brand;
    
        @Min(value = 1, message = "电话尺寸最小为1")
        @Max(value = 10, message = "电话尺寸最大为10")
        private Integer size;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public Integer getSize() {
            return size;
        }
    
        public void setSize(Integer size) {
            this.size = size;
        }
    }
    
    

    可以看到上面的代码中对字段"name"进行了非空校验,对字段"size"进行的大小的限制。


    controller:
    展开源码
    
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @PostMapping("/a")
        public ResponseData test(@RequestBody @Validated Phone phone) {
            return new ResponseData.ResponseDataBuilder<>().success().build();
        }
    }
    
    

    统一的异常处理类,主要用来捕捉所有的validate异常,统一返回值的结构:
    展开源码
    
    @ControllerAdvice
    public class MyExceptionHandler {
    
        private Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);
    
        @ExceptionHandler(ValidationException.class)
        @ResponseBody
        public ResponseData handleException(ValidationException exception) {
            String errorMessage = exception.getMessage();
            log.error(errorMessage);
            return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessage).build();
        }
    
    
        @ExceptionHandler(BindException.class)
        @ResponseBody
        public ResponseData handleException(BindException exception) {
            String errorMessageStr = getErrorMessageStr(exception);
            log.error(errorMessageStr);
    
            return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessageStr).build();
        }
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseBody
        public ResponseData handleException(MethodArgumentNotValidException exception) {
            String errorMessageStr = getErrorMessageStr(exception);
            log.error(errorMessageStr);
    
            return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessageStr).build();
        }
    
        private String getErrorMessageStr(BindException exception) {
            List errorMessages = exception.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
            String errorMessageStr = errorMessages.stream().filter(message -> !StringUtils.isEmpty(message)).collect(Collectors.joining("--"));
            return errorMessageStr;
        }
    
        private String getErrorMessageStr(MethodArgumentNotValidException exception) {
            List errorMessages = exception.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
            String errorMessageStr = errorMessages.stream().filter(message -> !StringUtils.isEmpty(message)).collect(Collectors.joining("--"));
            return errorMessageStr;
        }
    
    }
    
    

    我们怎么简单的运用

    上面介绍的方法,可以对入参进行非业务的校验。但是在实际的项目中,我们还会遇到业务型的校验,比如:注册的时候判断用户名是否重复,这个时候我们就可以借鉴上面的思想进行一些优雅的校验。

    自定义一个校验异常

    展开源码
    
    public class MyValidateException extends RuntimeException {
    
        public MyValidateException() {
            super();
        }
    
        public MyValidateException(String message) {
            super(message);
        }
    
        public MyValidateException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public MyValidateException(Throwable cause) {
            super(cause);
        }
    
        protected MyValidateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    
    

    在service层去进行业务校验
    展开源码
    
    @Service
    public class TestService {
    
        List list = new ArrayList<>(Arrays.asList("Huawei"));
    
        public void test(Phone phone) {
            if (list.contains(phone.getName())) {
                throw new MyValidateException("手机的名字已存在");
            }
        }
    }
    
    

    统一的异常处理
    展开源码
    
    @ControllerAdvice
    public class MyExceptionHandler {
    
        private Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);
    
        @ExceptionHandler(MyValidateException.class)
        @ResponseBody
        public ResponseData handleException(MyValidateException exception) {
            String errorMessage = exception.getMessage();
            log.error(errorMessage);
            return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessage).build();
        }
    
    }
    
    
  • 相关阅读:
    单元測试和白盒測试相关总结
    数据结构:图的实现--邻接矩阵
    Android提示版本号更新操作流程
    《集体智慧编程》代码勘误:第六章
    LINUX设备驱动程序笔记(三)字符设备驱动程序
    数学定理证明机械化的中国学派(II)
    《Java并发编程实战》第三章 对象的共享 读书笔记
    Linux系列-安装经常使用软件
    Kubuntu 初始配置
    虚拟互换(virtual swap)
  • 原文地址:https://www.cnblogs.com/lwmp/p/13940296.html
Copyright © 2020-2023  润新知