springboot自定义异常处理

项目中我们是一定要碰到的情况就是无论在控制层,业务层还是Dao层都需要校验一些数据,无论是前端传过来的,还是经过业务处理判断的,如果不合法需要友好的提示给用户,否则用户收到一个NullPointerException这玩意,他可能会很懵逼,再说直接将错误的信息直接暴露给用户,这样的体验也不是太好。通过统一异常拦截,可以手动抛出异常,异常拦截格式化异常返回数据。

一、集成统一异常处理

  • 响应结果统一封装类
package com.hld.free.common;



import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;

import java.io.Serializable;
@Data
@ApiModel("Http结果响应")
public class HttpResult<T> implements Serializable {

    private static String successMessage = "操作成功";

    private static String errorMessage = "操作失败";

    @ApiModelProperty("响应码")
    private int code;

    @ApiModelProperty("响应数据")
    private T data;

    @ApiModelProperty("响应消息")
    private String msg;

    public HttpResult(){ }

    public HttpResult(int code, String msg) {
        this.code=code;
        this.msg=msg;
    }

    public HttpResult(int code, String msg, T data) {
        this.code=code;
        this.msg=msg;
        this.data=data;
    }

    public static HttpResult successResult() {
        return new HttpResult(HttpStatus.OK.value(), successMessage);
    }

    public static HttpResult successResult(String msg) {
        return new HttpResult(DmStatus.OK.getHttpStatus().value(), defaultSuccessMsg(msg));
    }

    public static HttpResult successResult(Object obj) {
        return new HttpResult(DmStatus.OK.getHttpStatus().value(), successMessage, obj);
    }

    public static HttpResult successResult(String msg, Object obj) {
        return new HttpResult(DmStatus.OK.getHttpStatus().value(), defaultSuccessMsg(msg), obj);
    }

    public static HttpResult failureResult() {
        return new HttpResult(DmStatus.INTERNAL_ERROR.getHttpStatus().value(), errorMessage);
    }

    public static HttpResult failureResult(String msg) {
        return new HttpResult(DmStatus.INTERNAL_ERROR.getHttpStatus().value(), defautlErrorMsg(msg));
    }

    public static HttpResult failureResult(Integer code, String msg) {
        return new HttpResult(code, defautlErrorMsg(msg));
    }

    public static HttpResult failureResult(Integer code, String msg,Object obj) {
        return new HttpResult(code, defautlErrorMsg(msg),obj);
    }

    public static HttpResult failureResult(String msg, Object obj) {
        return new HttpResult(DmStatus.INTERNAL_ERROR.getHttpStatus().value(), defaultSuccessMsg(msg), obj);
    }

    private static String defautlErrorMsg(String msg) {
        return StringUtils.isEmpty(msg)?errorMessage:msg;

    }

    private static String defaultSuccessMsg(String msg) {
        return StringUtils.isEmpty(msg)?successMessage:msg;
    }
}
  • 异常状态信息枚举
package com.hld.free.common;

import java.text.MessageFormat;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;

public enum DmStatus {
    OK(HttpStatus.OK, "DM-200-00000", "数据返回正常"),
    BAD_REQUEST(HttpStatus.BAD_REQUEST, "DM-400-A0001", "{0}"),
    INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "DM-500-B0001", "后台服务异常,请联系管理员!"),
    NO_REGISTER_ERROR(HttpStatus.UNAUTHORIZED, "DM-500-B0001", "账号未注册"),
    NO_BINDING_ERROR(HttpStatus.FORBIDDEN, "DM-500-B0001", "账号未注册"),
    SYS_RUNTIME_ERROR(HttpStatus.REQUEST_TIMEOUT, "DM-400-B0002", "系统运行时异常,请联系管理员!"),
    DATA_OPT_ERROR(HttpStatus.BAD_REQUEST, "DM-400-B0003", "数据操作异常!{0}"),
    TARGET_OBJ_EXCEPTION(HttpStatus.BAD_REQUEST, "DM-400-B0004", "目标对象异常!"),
    GATEWAY_TIMEOUT(HttpStatus.GATEWAY_TIMEOUT, "DM-504-B0100", "网关超时【{0}】"),
    REQUEST_TIMEOUT(HttpStatus.REQUEST_TIMEOUT, "DM-408-B0101", "系统请求超时【{0}】"),
    PARAM_IS_INVALID(HttpStatus.BAD_REQUEST, "DM-400-B0200", "请求参数异常错误!【{0}】"),
    CONSTRAINT_VIOLATION_ERROR(HttpStatus.BAD_REQUEST, "DM-400-B0201", "未绑定对象!【{0}】"),
    SERVLET_REQUEST_BINDING_ERROR(HttpStatus.BAD_REQUEST, "DM-400-B0202", "缺失必要参数!【{0}】"),
    HTTP_METHOD_NOT_ALLOW_ERROR(HttpStatus.METHOD_NOT_ALLOWED, "DM-405-B0203", "HTTP请求方法不被允许【{0}】"),
    HTTP_MEDIA_TYPE_NOT_SUPPORTED_ERROR(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "DM-415-B0203", "不支持的媒体类型"),
    MAX_UPLOAD_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "DM-400-B0300", "文件大小超出【{0}】限制, 请压缩或降低文件质量!!"),
    MAX_UPLOAD_TYPE_ERROR(HttpStatus.BAD_REQUEST, "DM-400-B0301", "文件上传类型错误!【{0}】"),
    THIRD_SERVER_ERROR(HttpStatus.BAD_REQUEST, "DM-400-C0001", "调用第三方服务出错"),
    MIDDLE_CALL_ERROR(HttpStatus.BAD_REQUEST, "DM-400-C0100", "中间服务调用出错"),
    MESSAGE_SEND_ERROR(HttpStatus.BAD_REQUEST, "DM-400-C0120", "消息发送失败");

    private static final Logger LOG = LoggerFactory.getLogger(DmStatus.class);
    private final String errorMessage;
    private final String errorCode;
    private final HttpStatus httpStatus;

    private DmStatus(HttpStatus httpStatus, String errorCode, String errorMessage) {
        this.errorMessage = errorMessage;
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }

    public String getFormattedErrorMessage(String... params) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== DmErrorCode.getMessage(%s)", Arrays.toString(params)));
        }

        MessageFormat mf = new MessageFormat(this.errorMessage);
        String result = mf.format(params);
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> DmErrorCode.getMessage(%s): %s", Arrays.toString(params), result));
        }

        return result;
    }

    public String getErrorMessage() {
        return this.errorMessage;
    }

    public String getErrorCode() {
        return this.errorCode;
    }

    public HttpStatus getHttpStatus() {
        return this.httpStatus;
    }
}
  • 自定义异常处理类
    例如:自定义未注册异常
package com.hld.free.config;


import com.hld.free.common.CommonEnum;
import lombok.Data;

/**
 * @description: 未注册异常
 * @author: hld
 * @create: 2020/10/24 13:09
 * @update: 2020/10/24 13:09
 */
@Data
public class NoRegisterException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * 描述:错误码
     */
    private int errorCode;

    /**
     * 描述:错误信息
     */
    private String errorMsg;

    public NoRegisterException() {
    }

    public NoRegisterException(String errorMsg) {
        super(errorMsg);
        this.errorMsg = errorMsg;
    }

    public NoRegisterException(int errorCode, String errorMsg) {
        super(String.valueOf(errorCode));
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public NoRegisterException(int errorCode, String errorMsg, Throwable cause) {
        super(String.valueOf(errorCode), cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

}

  • 统一异常拦截
    统一异常拦截主要由@ControllerAdvice 注解实现
package com.hld.free.config;

import com.hld.free.common.DmStatus;
import com.hld.free.common.HttpResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;

/**
 * @description: 统一异常处理
 * @author: fangzhao
 * @create: 2020/3/24 13:09
 * @update: 2020/3/24 13:09
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

	 /**
     * 需指定自定义异常处理类
     * @param exception
     * @return
     */
    @ExceptionHandler(NoRegisterException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public HttpResult<Object> sendErrorResponse(NoRegisterException exception){
        log.error("未注册账号异常: {}", getStackTrace(exception));

        return HttpResult.failureResult(exception.getErrorCode(),exception.getErrorMsg());
    }


    /**
     * 系统运行时异常
     * @param exception RuntimeException
     * @return 返回消息
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpResult<Object> sendErrorResponse(RuntimeException exception){
        log.error("系统运行时异常: {}", getStackTrace(exception));
        return HttpResult.failureResult(DmStatus.SYS_RUNTIME_ERROR.getHttpStatus().value(),exception.getLocalizedMessage());
    }

    /**
     * 单个参数校验(没有绑定对象)
     * @param e ConstraintViolationException
     * @return 返回的信息
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpResult<Object> otherValidException(ConstraintViolationException e) {
        log.error("参数校验异常: {}", getStackTrace(e));
        return HttpResult.failureResult(DmStatus.CONSTRAINT_VIOLATION_ERROR.getHttpStatus().value(),e.getLocalizedMessage());
    }

    /**
     * 必填参数缺失
     * @param e ServletRequestBindingException
     * @return 返回的信息
     */
    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpResult<Object> servletRequestBinding(ServletRequestBindingException e) {
        log.error("必填参数缺失异常: {}", getStackTrace(e));
        return HttpResult.failureResult(DmStatus.SERVLET_REQUEST_BINDING_ERROR.getHttpStatus().value(),e.getLocalizedMessage());
    }

    /**
     * 参数错误异常
     * @param e 异常参数
     * @return 返回的信息
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public HttpResult<Object> handleException(Exception e) {
        StringBuilder errorMsg = new StringBuilder();
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e;
            BindingResult result = validException.getBindingResult();
            if (result.hasErrors()) {
                List<ObjectError> errors = result.getAllErrors();
                errors.forEach(p ->{
                    FieldError fieldError = (FieldError) p;
                    errorMsg.append(fieldError.getDefaultMessage()).append(",");
                });
            }
        } else if (e instanceof BindException) {
            BindException bindException = (BindException)e;
            if (bindException.hasErrors()) {
                log.error("请求参数错误: {}", bindException.getAllErrors());
                errorMsg.append(bindException.getAllErrors());
            }
        }
        log.error("参数校验异常: {}", getStackTrace(e));
        return HttpResult.failureResult(DmStatus.PARAM_IS_INVALID.getHttpStatus().value(),errorMsg.toString());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public HttpResult<Object> sendErrorResponse(HttpRequestMethodNotSupportedException exception){
        log.error("http方法不允许: {}", getStackTrace(exception));
        return HttpResult.failureResult(DmStatus.HTTP_METHOD_NOT_ALLOW_ERROR.getHttpStatus().value(), Arrays.toString(exception.getSupportedMethods()));
    }
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public HttpResult<Object> sendErrorResponse(HttpMediaTypeNotSupportedException exception){
        return HttpResult.failureResult(DmStatus.HTTP_MEDIA_TYPE_NOT_SUPPORTED_ERROR.getHttpStatus().value(),exception.getLocalizedMessage());
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public HttpResult<Object> sendErrorResponse(MaxUploadSizeExceededException exception){
        return HttpResult.failureResult(DmStatus.MAX_UPLOAD_SIZE_EXCEEDED.getHttpStatus().value(),exception.getLocalizedMessage());
    }

    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        throwable.printStackTrace(pw);
        return sw.getBuffer().toString();
    }
}

二、测试统一异常

  • 手动抛出异常
		/**
		  *这里只写出部分使用的代码片段
		  */
		//如果有@IgnoreAuth注解,则不验证token
        if (annotation != null) {
            return true;
        }
        //从header中获取token
        String token = request.getHeader(ShopCommon.TOKEN);
        //token为空
        if (StringUtils.isBlank(token)) {
            throw new NoRegisterException(401,"请先登录");
        }
  • 接口返回示例
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值