一、集成统一异常处理项目中我们是一定要碰到的情况就是无论在控制层,业务层还是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 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;
@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;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler(NoRegisterException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public HttpResult
二、测试统一异常
- 手动抛出异常
//如果有@IgnoreAuth注解,则不验证token
if (annotation != null) {
return true;
}
//从header中获取token
String token = request.getHeader(ShopCommon.TOKEN);
//token为空
if (StringUtils.isBlank(token)) {
throw new NoRegisterException(401,"请先登录");
}
- 接口返回示例



