zoukankan      html  css  js  c++  java
  • springboot-web进阶(三)——统一异常处理

    补充

      springboot中也是一样的可以对结果进行统一格式的包装,这样也就方便了前台的统一接收处理了;

      1.结果集包装类 

    package com.example.demo.bean;
    
    /**
     * 结果包装
     *
     * @author zcc ON 2018/2/9
     **/
    public class Result<T> {
        /**
         * 错误代码(可以设定例如500表示错误)
         */
        private Integer code;
        /**
         * 提示信息
         */
        private String msg;
        /**
         * 数据内容
         */
        private T data;
    
        public Result() {
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    Resultde

        可以参考之前SSM中的包装类http://www.cnblogs.com/jiangbei/p/7080955.html

      2.使用包装结果集

    @PostMapping(value = "/girls")
        public Result<Girl> addGirl(@Valid Girl girl, BindingResult bindingResult) {
            Result<Girl> result = new Result<>();
            // 表单验证逻辑
            if (bindingResult.hasErrors()) {
                List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                StringBuffer sb = new StringBuffer();
                for (FieldError fieldError : fieldErrors) {
                    sb.append(fieldError.getDefaultMessage());
                }
                result.setCode(500);
                result.setMsg(sb.toString());
                return result;
            }
            result.setCode(200);
            result.setMsg("success");
            result.setData(girlRepository.save(girl));
            return result;
        }

        3。改进结果集

          通过代码观察或者对比SSM中的示例,都发现这样写在代码中是挺傻的,这样,我们提供一个工具类(或者在结果集中直接封装)

    /**
     * 结果集的工具类
     *
     * @author zcc ON 2018/2/9
     **/
    public class ResultUtils {
    
        public static Result<Object> success(Object data) {
            Result<Object> result = new Result<>();
            result.setCode(200);
            result.setMsg("success");
            result.setData(data);
    
            return result;
        }
    
        public static Result<Object> error(String msg) {
            Result<Object> result = new Result<>();
            result.setCode(500);
            result.setMsg(msg);
            return result;
        }
    }

        使用:

     @PostMapping(value = "/girls")
        public Result<Object> addGirl(@Valid Girl girl, BindingResult bindingResult) {
            // 表单验证逻辑
            if (bindingResult.hasErrors()) {
                List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                StringBuffer sb = new StringBuffer();
                for (FieldError fieldError : fieldErrors) {
                    sb.append(fieldError.getDefaultMessage());
                }
                return ResultUtils.error(sb.toString());
            }
            return ResultUtils.success(girlRepository.save(girl));
        }

    一、概述

      springboot提供了默认的统一异常处理,basicErrorController,这个controller提供了默认了错误处理方法,包括错误跳转的路径和渲染方法。

      因为默认的错误处理方法可能会不适合项目的需要,所以当需要自定义统一错误处理的时候,可以继承basicErrorController。或重新实现basicErrorController.

    统一异常处理可配置400,404,500等状态异常错误处理。basicErrorController内部提供了针对accept=**的不同处理方法,可区别是普通调用还是ajax等其他调用。

      缺点:需要重写basicErrorController。并配置覆盖原来springboot的默认配置。当使用EmbeddedServletContainerCustomizer类配置400,404。500的时候,暂时不清楚调用springboot的处理方法。

    二、问题提出

      假设需要根据girl的不同age作出不同的动作,controller如下:

     @GetMapping(value = "/girls/getAge/{id}")
        public void getAge(@PathVariable("id") Integer id) {
            // 判断应该交给service
            

      service:

    public void getAge(Integer id) {
            Integer age = girlRepository.findOne(id).getAge();
            if (age < 12) {
                // 小学生
            } else {
                // 上初中了
            }
        }

      如果只是简单的判断,不同年龄返回不同信息给前台,那确实可以通过返回一个String来进行

      但是,如果业务比较复杂,需要的不仅仅是返回一个字符串,而是需要其他动作,这个时候就显得有点力不从心了!

    三、统一异常处理

       1.基本用法

       controller调用service,抛出异常即可:

     @GetMapping(value = "/girls/getAge/{id}")
        public void getAge(@PathVariable("id") Integer id) throws Exception{
            // 判断应该交给service
            girlService.getAge(id);
        }

      service也是,该抛异常就抛出异常:

    public void getAge(Integer id) throws Exception{
            Integer age = girlRepository.findOne(id).getAge();
            if (age < 12) {
                // 小学生
                throw new Exception("小学生!");
            } else {
                // 上初中了
                throw new Exception("初中生!");
            }
        }

      统一异常处理类:

    package com.example.demo.handle;
    
    import com.example.demo.bean.Result;
    import com.example.demo.utils.ResultUtils;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * 异常处理类
     * 添加@ControllerAdvice标记为异常处理类
     * @author zcc ON 2018/2/9
     **/
    @ControllerAdvice
    public class MyExceptionHandler {
    
        /**
         * 如果不在这里加@ResponseBody,则需要在类上加
         * @param e 异常
         * @return
         */
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public Result handle(Exception e) {
            return ResultUtils.error(e.getMessage());
        }
    }

      2.自定义异常

        写自定义异常还是之前的基础篇的老套路,基本就是几个构造方法的编写,再根据需要加上自己的变量!

    package com.example.demo.exception;
    
    /**
     * 自定义异常
     * 继承RuntimeException才能使得spring进行异常时的回滚
     * @author zcc ON 2018/2/9
     **/
    public class MyException extends RuntimeException{
        private Integer code;
    
        public MyException(Integer code, String msg) {
            super(msg);
            this.code = code;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    }

        异常处理类:

    package com.example.demo.handle;
    
    import com.example.demo.bean.Result;
    import com.example.demo.exception.MyException;
    import com.example.demo.utils.ResultUtils;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * 异常处理类
     * 添加@ControllerAdvice标记为异常处理类
     * @author zcc ON 2018/2/9
     **/
    @ControllerAdvice
    public class MyExceptionHandler {
    
        /**
         * 如果不在这里加@ResponseBody,则需要在类上加
         * @param e 异常
         * @return
         */
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public Result handle(Exception e) {
            // 先判断捕获的是不是我们定义的异常
            if (e instanceof MyException) {
                MyException me = (MyException) e;
                return ResultUtils.exp(me.getCode(), me.getMessage());
            } else {
                return ResultUtils.error("未知错误!");
            }
        }
    }

      其他地方需要注意:

        既然MyException继承RuntimeException了,那controller和service就不用抛出异常了

        ResultUtils根据需要微调一些

      统一404、500异常处理

      @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
            logger.error("", e);
            ResponseData r = new ResponseData();
            r.setMessage(e.getMessage());
            if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
                 r.setCode(404);
            } else {
                 r.setCode(500);
            }

    三、使用枚举优化管理

      1.枚举

    package com.example.demo.enums;
    
    /**
     * 结果集的枚举管理类
     * 不用给出setter了,因为枚举不会再去set了
     * @author zcc
     **/
    public enum ResultEnum {
        UNKNOWN(-1, "未知错误"),
        SUCCESS(200, "success"),
        PRIMARY(100, "小学生"),
        MIDDLE(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;
        }
    }

      更多枚举基础知识,参考:http://www.cnblogs.com/jiangbei/p/7580482.html

      2.调整自定义异常的构造方法

    /**
     * 自定义异常
     * 继承RuntimeException才能使得spring进行异常时的回滚
     * @author zcc ON 2018/2/9
     **/
    public class MyException extends RuntimeException{
        private Integer code;
    
        public MyException(ResultEnum resultEnum) {
            super(resultEnum.getMsg());
            this.code = resultEnum.getCode();
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    }

      3.使用自定义枚举统一管理错误代码和错误信息之间的关系

     public void getAge(Integer id) {
            Integer age = girlRepository.findOne(id).getAge();
            if (age < 12) {
                // 小学生
                throw new MyException(ResultEnum.PRIMARY);
            } else {
                // 上初中了
                throw new MyException(ResultEnum.MIDDLE);
            }
        }
  • 相关阅读:
    Java 异常处理
    Java Scanner 类
    Java 流(Stream)、文件(File)和IO
    Java 方法
    'javac' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
    Java 正则表达式
    nginx配置ssl证书
    Windows 查看端口、根据pid查找引用程序
    scp复制文件到另外一台服务器
    CentOS安装jdk SE 1.8 JDK与OpenJDK的区别
  • 原文地址:https://www.cnblogs.com/jiangbei/p/8436565.html
Copyright © 2011-2022 走看看