我们在做 Web 应用的时候,请求处理过程中发生错误异常是一个非常常见的情况,但是异常的处理方式和放回的异常内容并没有做一个统一的处理。在现在这种前后端分离的时代,不论响应成功还是失败的数据格式都需要保持一致。
1. 返回格式统一封装
Api.java
package com.xkcoding.scaffold.common;
import com.xkcoding.scaffold.common.status.Status;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* API 统一返回格式封装
* </p>
*
* @package: com.xkcoding.scaffold.common
* @description: API 统一返回格式封装
* @author: yangkai.shen
* @date: Created in 2018/8/2 下午6:15
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
public class Api implements Serializable {
private static final long serialVersionUID = 1672493411204459264L;
/**
* 返回码
*/
private Integer code;
/**
* 返回信息
*/
private String msg;
/**
* 返回数据
*/
private Object data;
/**
* 默认构造
*/
public Api() {
this.code = Status.SUCCESS.getCode();
this.msg = Status.SUCCESS.getMsg();
}
/**
* 构造 API 返回对象
*
* @param code 返回码
* @param msg 返回信息
* @param data 数据
*/
public Api(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 通用构造包含返回信息的 Api <br>
* 主要用于只包含返回信息,不包含数据时的返回
*
* @param code 状态码
* @param msg 信息
* @return Api
*/
public static Api of(int code, String msg, Object data) {
return new Api(code, msg, data);
}
/**
* 通用构造包含返回信息的 Api <br>
* 主要用于只包含返回信息,不包含数据时的返回
*
* @param code 状态码
* @param msg 信息
* @return Api
*/
public static Api ofMessage(int code, String msg) {
return new Api(code, msg, null);
}
/**
* 通用构造包含返回信息的 Api <br>
* 主要用于只包含返回信息,不包含数据时的返回
*
* @param msg 信息
* @return Api
*/
public static Api ofMessage(String msg) {
return new Api(Status.SUCCESS.getCode(), msg, null);
}
/**
* 通用构造包含返回数据的 Api <br>
* 主要用于操作成功时的返回(不带数据)
*
* @return Api
*/
public static Api ofSuccess() {
return new Api(Status.SUCCESS.getCode(), Status.SUCCESS.getMsg(), null);
}
/**
* 通用构造包含返回数据的 Api <br>
* 主要用于操作成功时的返回
*
* @param data 操作成功时需要返回的数据
* @return Api
*/
public static Api ofSuccess(Object data) {
return new Api(Status.SUCCESS.getCode(), Status.SUCCESS.getMsg(), data);
}
/**
* 通过 Status 构造 Api <br>
* 主要用于出现异常时的返回
*
* @param status {@link Status}
* @return Api
*/
public static Api ofStatus(Status status) {
return new Api(status.getCode(), status.getMsg(), null);
}
}
2. 通用状态统一封装
Status.java
package com.xkcoding.scaffold.common.status;
import com.xkcoding.scaffold.common.BaseEnum;
import lombok.Getter;
/**
* <p>
* 状态码枚举
* </p>
*
* @package: com.xkcoding.scaffold.common
* @description: 状态码枚举
* @author: yangkai.shen
* @date: Created in 2018/8/2 下午7:49
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Getter
public enum Status implements BaseEnum {
/**
* 成功
*/
SUCCESS(200, "成功"),
/**
* 请求错误
*/
BAD_REQUEST(400, "请求错误"),
/**
* 尚未登录
*/
UNAUTHORIZED(401, "尚未登录"),
/**
* 权限不够
*/
FORBIDDEN(403, "权限不够"),
/**
* 请求不存在
*/
REQUEST_NOT_FOUND(404, "请求不存在"),
/**
* 服务器内部错误
*/
INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
/**
* 用户名或密码错误
*/
USERNAME_OR_PASSWORD_ERROR(50000, "用户名或密码错误"),
/**
* 用户不存在
*/
USER_NOT_EXIST(50001, "账号不存在"),
/**
* 用户已被禁用
*/
USER_DISABLE(50002, "账号已被禁用"),
/**
* 账号已删除
*/
USER_DELETED(50003, "账号已删除"),
/**
* 验证码错误
*/
VERIFY_CODE_ERROR(50004, "验证码错误"),
/**
* 登录成功
*/
LOGIN_SUCCESS(50005, "登录成功"),
/**
* 退出成功
*/
LOGOUT_SUCCESS(50006, "退出成功"),
/**
* 验证码异常
*/
CODE_ERROR(50007, "验证码异常"),
/**
* 获取验证码的值失败
*/
CODE_GET_ERROR(50008, "获取验证码的值失败"),
/**
* 验证码的值不能为空
*/
CODE_VALUE_NOT_NULL(50009, "验证码的值不能为空"),
/**
* 验证码不存在
*/
CODE_NOT_FOUND(50010, "验证码不存在"),
/**
* 验证码已过期
*/
CODE_IS_EXPIRED(50011, "验证码已过期"),
/**
* 验证码不匹配
*/
CODE_NOT_MATCH(50012, "验证码不匹配"),
/**
* 验证码发送异常
*/
CODE_SEND_ERROR(50012, "验证码发送异常");
private Integer code;
private String msg;
Status(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
3. 自定义业务异常类
ScaffoldException.java
package com.xkcoding.scaffold.exception;
import com.xkcoding.scaffold.common.status.Status;
import lombok.Getter;
/**
* <p>
* 通用全局异常
* </p>
*
* @package: com.xkcoding.scaffold.exception
* @description: 通用全局异常
* @author: yangkai.shen
* @date: Created in 2018/8/2 下午7:59
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Getter
public class ScaffoldException extends Exception {
/**
* 异常码
*/
private Integer code;
/**
* 返回信息
*/
private String msg;
/**
* 返回内容
*/
private Object data;
public ScaffoldException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public ScaffoldException(Integer code, String msg, Object data) {
super(msg);
this.code = code;
this.msg = msg;
this.data = data;
}
public ScaffoldException(Status status) {
super(status.getMsg());
this.code = status.getCode();
this.msg = status.getMsg();
}
public ScaffoldException(Status status, Object data) {
super(status.getMsg());
this.code = status.getCode();
this.msg = status.getMsg();
this.data = data;
}
}
4. 配置 Spring Boot
配置 application.yml 主要是为了对404错误作处理
spring:
### 开启404异常抛出 ###
mvc:
throw-exception-if-no-handler-found: true
### 关闭静态资源映射 ###
resources:
add-mappings: false
5. 统一异常处理
GlobalExceptionHandler.java
package com.xkcoding.scaffold.handler;
import com.xkcoding.scaffold.common.Api;
import com.xkcoding.scaffold.common.status.Status;
import com.xkcoding.scaffold.exception.ScaffoldException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;
/**
* <p>
* 全局异常处理
* </p>
*
* @package: com.xkcoding.scaffold.handler
* @description: 全局异常处理
* @author: yangkai.shen
* @date: Created in 2018/8/2 下午8:05
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Api handlerException(Exception e) {
if (e instanceof NoHandlerFoundException) {
log.error("【全局异常拦截】NoHandlerFoundException: 请求方法 {}, 请求路径 {}", ((NoHandlerFoundException) e).getRequestURL(), ((NoHandlerFoundException) e).getHttpMethod());
return Api.ofStatus(Status.REQUEST_NOT_FOUND);
} else if (e instanceof ScaffoldException) {
log.error("【全局异常拦截】ScaffoldException: 状态码 {}, 异常信息 {}", ((ScaffoldException) e).getCode(), e.getMessage());
return new Api(((ScaffoldException) e).getCode(), e.getMessage(), ((ScaffoldException) e).getData());
} else if (e instanceof AccessDeniedException) {
log.error("【全局异常拦截】AccessDeniedException: 异常信息 {}", e.getMessage());
return Api.ofStatus(Status.FORBIDDEN);
}
log.error("【全局异常拦截】: 异常信息 {} ", e.getMessage());
return Api.ofStatus(Status.INTERNAL_SERVER_ERROR);
}
}
Tip
异常处理的时候,务必在全局异常处理类中,打印异常日志,打印异常日志,打印异常日志!