美文网首页
Spring Boot 进阶之 Web 进阶笔记

Spring Boot 进阶之 Web 进阶笔记

作者: solocoder | 来源:发表于2018-11-17 11:11 被阅读0次

    一、表单验证

    改造 Girl 对象,给 age 字段加校验最小值为18,并给出错误提示。

    @Entity
    public class Girl {
    
        @Id
        @GeneratedValue
        private Integer id;
    
        private String cupSize;
    
        @Min(value = 18, message = "未成年少女禁止入内")
        private Integer age;
    
        public Girl() {
        }
        
        //getter、setter 方法省略
    }
    

    相应的 Controller 方法,用 Girl 对象接收传递过来的参数,并加上 @Validated 注解。BindingResult 用来接收错误信息

    @PostMapping(value = "/girl/add")
    public Girl add(@Validated Girl girl, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }
        return repository.save(girl);
    }
    

    如果传递的 age 小于 18,则会报错。

    注意:@NotNull 和 @NotEmpty 和@NotBlank 区别

    @NotEmpty 用在集合类上面
    @NotBlank 用在String上面
    @NotNull 用在基本类型上

    二、使用 AOP 处理请求

    先在 pom.xml 文件中添加 AOP 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    

    新建 HttpAspect.java

    package com.solo.test01.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Aspect
    @Component
    public class HttpAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(HttpAspect.class);
    
        //指定切点
        @Pointcut("execution(public * com.solo.test01.controller.GirlController.*(..))")
        public void log() {
    
        }
    
        @Before("log()")
        public void doBefore(JoinPoint joinPoint) {
            logger.info("doBefore-----");
            
            //获取 http 请求的内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //url
            logger.info("url: {}", request.getRequestURL());
            //method
            logger.info("method: {}", request.getMethod());
            // ip
            logger.info("ip: {}", request.getRemoteAddr());
            //方法
            logger.info("class_method: {}", joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
            //参数
            logger.info("params: {}", joinPoint.getArgs());
        }
    
        @After("log()")
        public void doAfter() {
            logger.info("doAfter-----");
        }
        
        @AfterReturning(returning = "object", pointcut = "log()")
        public void doAfterReturning(Object object){
            logger.info("response: {}", object.toString());
        }
    }
    

    @Pointcut 指定切点,在 @Before 中获取 http 请求的内容,@AfterReturning 获取返回的内容。

    三、指定统一返回格式

    创建 Result 对象为统一返回数据格式

    public class Result<T> {
        private Integer code;
        private String msg;
        private T data;
        
        // get, set方法省略
    }
    

    封装工具类 ResultUtils

    
    /**
     * 返回结果的工具类封装
     */
    public class ResultUtils {
    
        public static Result success(Object obj) {
            Result result = new Result();
            result.setCode(0);
            result.setMsg("成功");
            result.setData(obj);
            return result;
        }
    
        public static Result success() {
            return success(null);
        }
    
        public static Result error(Integer code, String msg) {
            Result result = new Result();
            result.setCode(code);
            result.setMsg(msg);
            return result;
        }
    }
    

    不同状态返回相同格式的结果

    @PostMapping(value = "/girl/add")
    public Result<Girl> add(@Validated Girl girl, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultUtils.error(1, bindingResult.getFieldError().getDefaultMessage());
        }
        return ResultUtils.success(repository.save(girl));
    }
    

    四、全局捕获异常处理

    假如在 Service 中抛出了异常

    @Component
    public class GirlService {
    
        @Autowired
        GirlRepository repository;
    
        public void getAge(Integer id){
            Girl girl = repository.findById(id).get();
            Integer age = girl.getAge();
            if(age <= 10){
                // 还在上小学吧
                throw new GirlException(ResultEnum.PRIMARY_SCHOOL);
            }else if(age > 10 && age < 16){
                // 还在上中学吧
                throw new GirlException(ResultEnum.MIDDLE_SCHOOL);
            }
        }
    }
    

    定义一个 ExceptionHandle 来全局捕获 GirlException 异常

    
    @ControllerAdvice
    public class ExceptionHandle {
    
        @ExceptionHandler(value = GirlException.class)
        @ResponseBody
        public Result handle(Exception e){
            if(e instanceof GirlException){
                GirlException girlException = (GirlException) e;
                return ResultUtils.error(girlException.getCode(), e.getMessage());
            }
            return ResultUtils.error(-1, "未知错误");
        }
    }
    

    这样当出现指定异常时就会被全局捕获到。

    GirlException 是自定义异常,代码如下:

    public class GirlException extends RuntimeException {
    
        private Integer code;
    
        public GirlException() {
        }
    
        public GirlException(ResultEnum resultEnum) {
            super(resultEnum.getMsg());
            this.code = resultEnum.getCode();
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    }
    

    要继承 RuntimeException ,不要继承 Exception ,否则事务不会回滚。

    将所有错误码和错误信息都封装到枚举类里,便于统一管理。

    public enum ResultEnum {
        UNKNOWN(-1, "未知错误"),
        SUCCESS(0, "成功"),
        PRIMARY_SCHOOL(100, "还是小学生吧"),
        MIDDLE_SCHOOL(101, "是中学生吧")
        ;
    
        private Integer code;
        private String msg;
    
        ResultEnum(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public String getMsg() {
            return msg;
        }
    }
    

    作者正在写一个有趣的开源项目 coderiver,致力于打造全平台型全栈精品开源项目。

    coderiver 中文名 河码,是一个为程序员和设计师提供项目协作的平台。无论你是前端、后端、移动端开发人员,或是设计师、产品经理,都可以在平台上发布项目,与志同道合的小伙伴一起协作完成项目。

    coderiver 河码 类似程序员客栈,但主要目的是方便各细分领域人才之间技术交流,共同成长,多人协作完成项目。暂不涉及金钱交易。

    计划做成包含 pc端(Vue、React)、移动H5(Vue、React)、ReactNative混合开发、Android原生、微信小程序、java后端的全平台型全栈项目,欢迎关注。

    项目地址:https://github.com/cachecats/coderiver

    您的鼓励是我前行最大的动力,欢迎点赞,欢迎送小星星✨ ~

    相关文章

      网友评论

          本文标题:Spring Boot 进阶之 Web 进阶笔记

          本文链接:https://www.haomeiwen.com/subject/jbubfqtx.html