Spring Boot异常统一处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Kevin_zhai/article/details/80436433

最近在学习自己搭建一个配置中心平台,准备用spring boot来搭建后台web系统,将遇到的问题在此记录。github项目地址:点击打开链接

我们在用ajax向服务端请求数据时,免不了会有异常。如果不进行统一处理,直接把异常信息抛到前端,界面会很不友好。spring boot可以通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常。

1.自定义异常类

首先,我们自定义一个异常类。在后台业务处理时,如果是可知的业务异常,我们直接抛出此类异常。当捕获到此类异常时,可以直接把异常信息返回到前端。代码如下:

package com.kevin.confcenter.common.exception;

/**
 * @Author: kevin
 * @Description: 基础异常类
 * @Date: Created In 2018/3/10 14:51
 */
public abstract class ConfCenterException extends RuntimeException {
    /**
     * uid
     */
    private static final long serialVersionUID = 8037891447646609768L;

    /**
     * 默认构造函数
     */
    public ConfCenterException() {
    }

    /**
     * 构造函数
     * @param errMsg 异常消息
     */
    public ConfCenterException(String errMsg) {
        super(errMsg);
    }

    /**
     * 构造函数
     * @param cause 原始异常
     */
    public ConfCenterException(Throwable cause) {
        super(cause);
    }

    /**
     * 构造函数
     * @param errMsg 异常消息
     * @param cause 原始异常
     */
    public ConfCenterException(String errMsg, Throwable cause) {
        super(errMsg, cause);
    }

}

2.自定义返回数据类

我们自定义一个返回结果类,包装返回前端数据信息,包括状态、错误信息、数据等,所有的ajax请求,数据都用此类包装后返回前端。我们统一处理异常时,也会返回这个类对象。这样,前端根据状态码,就可以做出相应的操作,抛出封装好的错误信息或者跳转到指定的页面。代码如下

package com.kevin.confcenter.common.bean.vo;

/**
 * 客户端的HTTP调用的应答结果类
 */
public class ResultInfo {

    /**
     * 应答结果状态码——成功
     */
    public static final int RESULT_CODE_SUCCESS = 0;
    /**
     * 应答结果状态码——通用错误
     */
    public static final int RESULT_CODE_COMMONERR = 9999;

    /**
     * session过期
     */
    public static final int RESULT_SESSION_TIMEOUT = 1;

    /**
     * 返回状态
     */
    private int status = RESULT_CODE_SUCCESS;

    /**
     * 返回状态描述
     */
    private String statusInfo = "SUCCESS"; // 操作结果描述信息

    /**
     * 返回数据
     */
    private Object data;// 操作返回数据绑定

    /**
     * 返回一个默认的错误结果
     *
     * @return 错误结果
     */
    public static ResultInfo error() {
        ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, "ERROR");
        return res;
    }

    /**
     * 返回一个带错误信息的错误结果
     *
     * @param errorMessage 错误信息
     * @return 错误结果
     */
    public static ResultInfo errorMessage(String errorMessage) {
        ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, errorMessage);
        return res;
    }

    /**
     * session过期
     *
     * @return
     */
    public static ResultInfo sessionTimeout() {
        ResultInfo res = new ResultInfo(RESULT_SESSION_TIMEOUT, "登录超时");
        return res;
    }

    /**
     * 返回一个带错误信息和数据的错误结果
     *
     * @param errorMessage 错误信息
     * @param data         数据
     * @return 错误结果
     */
    public static ResultInfo errorMessage(String errorMessage, Object data) {
        ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, errorMessage);
        res.setData(data);
        return res;
    }

    /**
     * 返回一个带状态和信息的结果
     *
     * @param status 状态
     * @param info   信息
     * @return 返回结果
     */
    public static ResultInfo result(int status, String info) {
        ResultInfo res = new ResultInfo();
        res.status = status;
        res.statusInfo = info;
        return res;
    }

    /**
     * 返回一个带状态,信息和数据的结果
     *
     * @param status 状态
     * @param info   信息
     * @param data   数据
     * @return 返回结果
     */
    public static ResultInfo result(int status, String info, Object data) {
        ResultInfo res = new ResultInfo();
        res.status = status;
        res.statusInfo = info;
        res.data = data;
        return res;
    }

    /**
     * 返回一个成功结果
     *
     * @return 成功结果
     */
    public static ResultInfo success() {
        ResultInfo res = new ResultInfo();
        return res;
    }

    /**
     * 返回一个带数据的成功结果
     *
     * @param data 数据
     * @return 成功结果
     */
    public static ResultInfo success(Object data) {
        ResultInfo res = new ResultInfo();
        res.setData(data);
        return res;
    }

    /**
     * 返回一个带信息的成功结果
     *
     * @param message 提示信息
     * @return 成功结果
     */
    public static ResultInfo successMessage(String message) {
        ResultInfo res = new ResultInfo(RESULT_CODE_SUCCESS, message);
        return res;
    }

    /**
     * 默认构造函数
     */
    public ResultInfo() {
    }

    /**
     * 带状态和信息的构造函数
     *
     * @param status     状态
     * @param statusInfo 提示信息
     */
    public ResultInfo(int status, String statusInfo) {
        this.status = status;
        this.statusInfo = statusInfo;
    }

    /**
     * 带状态,信息和数据的构造函数
     *
     * @param status     状态
     * @param statusInfo 提示信息
     * @param data       数据
     */
    public ResultInfo(int status, String statusInfo, Object data) {
        super();
        this.status = status;
        this.statusInfo = statusInfo;
        this.data = data;
    }

    public Object getData() {
        return data;
    }

    public int getStatus() {
        return status;
    }

    public String getStatusInfo() {
        return statusInfo;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setStatusInfo(String statusInfo) {
        this.statusInfo = statusInfo;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((data == null) ? 0 : data.hashCode());
        result = prime * result + status;
        result = prime * result + ((statusInfo == null) ? 0 : statusInfo.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ResultInfo other = (ResultInfo) obj;
        if (data == null) {
            if (other.data != null) {
                return false;
            }
        } else if (!data.equals(other.data)) {
            return false;
        }
        if (status != other.status) {
            return false;
        }
        if (statusInfo == null) {
            if (other.statusInfo != null) {
                return false;
            }
        } else if (!statusInfo.equals(other.statusInfo)) {
            return false;
        }
        return true;
    }
}

3.异常统一处理

使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常。代码如下:

package com.kevin.confcenter.admin.extend;

import com.kevin.confcenter.common.bean.vo.ResultInfo;
import com.kevin.confcenter.common.exception.ConfCenterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Author: kevin
 * @Description: 异常统一处理
 * @Date: Created In 2018/4/16 10:25
 */
@ControllerAdvice
public class ExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandler.class);

    @org.springframework.web.bind.annotation.ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultInfo handler(Exception e) {
        if (e instanceof ConfCenterException) {
            return ResultInfo.errorMessage(e.getMessage());
        } else {
            LOGGER.error("exception:{}", e.getMessage(), e);
            return ResultInfo.errorMessage("服务器内部错误");
        }
    }
}

4.前端ajax封装

前端封装ajax方法,可以实现防重复提交和针对错误码进行相应的处理。代码如下:

// 系统全局的ajax队列
    ajax_queue: [],
    ajax: function (options) {
        if (options.lu_ajax_id) {
            // 检查之前的req是否已经完成
            if (conf.utils.ajax_queue.contains(options.lu_ajax_id)) {
                $.fn.alert('不要频繁重复操作,请稍后再试.');
                return;
            }
            // complete回调,用于移除之前的ajax queue中的请求标记
            options.complete = function (jqXHR, textStatus) {
                var index = conf.utils.ajax_queue.indexOf(options.lu_ajax_id);
                if (index > -1) {
                    conf.utils.ajax_queue.splice(index, 1);
                }
            }

            conf.utils.ajax_queue.push(options.lu_ajax_id);
        }

        if (!options.dataType) {
            options.dataType = "json";
        }

        if (!options.timeout) {
            options.timeout = 1000 * 60 * 60;
        }

        if (!options.timeout) {
            options.timeout = 1000 * 60 * 3;
        }

        if (options.url.indexOf('?') > -1) {//加入时间戳
            options.url += '&' + new Date().getTime();
        } else {
            options.url += '?' + new Date().getTime();
        }

        if (!options.success) { //没有加入自定义的success回调函数,则调用默认回调函数
            options.success = function (res, textStatus, jqXHR) {
                if (res.status != 0) { //如果返回结果消息状态码非零则表示失败,弹出错误信息
                    if (res.status == 1) {
                        window.location.href = "/user/login";
                    } else {
                        if (options.fail) {
                            options.fail.call(this, res, textStatus, jqXHR);
                            return;
                        }
                        $.fn.alert(res.statusInfo);
                        return;
                    }
                }

                if (options.ok) { // 如果有自定义ok回调,则在结果码为成功时回调
                    options.ok.call(this, res, textStatus, jqXHR);
                }
            }
        }
        if (!options.error) { // 没有加入自定义的error回调函数,则指定默认回调
            options.error = function (res, textStatus, jqXHR) {
                $.fn.alert("ERROR:" + jqXHR);
            }
        }

        return $.ajax(options);
    }

5.实例

我们以登录接口为例,先在controller加一个登录方法,不做任何处理,直接抛出BusinessException异常,BusinessException是继承自ConfCenterException类,代码如下:

/**
     * 登录
     *
     * @param
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public ResultInfo login(HttpServletRequest request, String userName, String password) {
        throw new BusinessException("test");
    }

前端ajax请求如下:

conf.utils.ajax({
            url: '/user/login',
            type: 'POST',
            async: false,
            data: data,
            ok: function (res, textStatus, jqXHR) {
                if (res.status == 0) {
                    window.location.href = "/index";
                }
            }
        });

当我们在前端点击登录时,就会弹窗提示,直接显示我们的异常信息,效果如下:



展开阅读全文

没有更多推荐了,返回首页