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);
            }
        }
  • 相关阅读:
    【HTML5 绘图与动画】使用canvas
    【H5新增元素和文档结构】新的全局属性 1. contentEditable 可编辑内容 2. contextmenu 快捷菜单 3. data 自定义属性 4. draggable 可拖动 5. dropzone 拖动数据 6. hidden 隐藏 7. spellcheck 语法检查 8. translate 可翻译
    【H5新增元素和文档结构】完善旧元素 1. a 超链接 2. ol 有序列表 3. dl 定义列表 4. cite 引用文本 5. small 小号字体 6. iframe 浮动框架 7. script 脚本
    【H5新增元素和文档结构】新的语义信息 1. address 2. time 3. figure 跟 figcaption 4. details 和 summary 5. mark 6. progress 7. meter 8. dialog 9.bdi 10. wbr 11. ruby、rt、rp 12. command
    【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条 4. aside 辅助栏 5. main 主要区域 6. header 标题栏 7. hgroup 标题组 8. footer 页脚栏
    5_PHP数组_3_数组处理函数及其应用_9_数组集合运算函数
    【华为云技术分享】鲲鹏弹性云服务器GCC交叉编译环境搭建指南
    【华为云技术分享】7 分钟全面了解位运算
    【华为云技术分享】Linux内核编程环境 (1)
    【华为云技术分享】华为云MySQL 8.0正式商用,全新增强版开源利器强势来袭
  • 原文地址:https://www.cnblogs.com/jiangbei/p/8436565.html
Copyright © 2011-2022 走看看