需求源自于任何一个业务的编写总会有各种各样的条件判断,需要时时手动抛出异常,又希望让接口返回友好的错误信息。
spring boot提供的帮助是自动将异常重定向到路由为/error的控制器
但是我们又希望手动抛出的异常与正常的数据返回为同一类型
所以我的解决方案由三个步骤组成:
1.一个异常枚举类 StatusCodeEnum.java
public enum StatusCodeEnum implements Serializable {
SUCCESS(0, "成功"),
ERROR(-1, "失败"),
USER_INVALID(60000, "无效用户"),
SYS_ARG_INVALID(11000, "无效参数"),
;
private static final long serialVersionUID = 1L;
private int code;
private String msg;
StatusCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
//根据code获取对应枚举
public static StatusCodeEnum getByCode(int code) {
StatusCodeEnum[] values = StatusCodeEnum.values();
for (StatusCodeEnum bizStatusCodeEnum : values) {
if (bizStatusCodeEnum.code == code) {
return bizStatusCodeEnum;
}
}
return null;
}
}
2.一个异常调用类 BaseException.java
因为抛出异常只能抛出字符串 所以这里使用了com.alibaba.fastjson.JSON包
public class BaseException {
private int code;
private String message;
public static void error(StatusCodeEnum statusCodeEnum) throws Exception
{
error(statusCodeEnum.getCode(),statusCodeEnum.getMsg());
}
public static void error(String message) throws Exception
{
error(-1,message);
}
public static void error(int code,String message) throws Exception
{
BaseException baseException = new BaseException();
baseException.setCode(code);
baseException.setMessage(message);
throw new Exception(JSON.toJSONString(baseException)) ;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "BaseException{" +
"code=" + code +
", message='" + message + '\'' +
'}';
}
}
3.异常处理控制器 ErrorController.java
@Controller
public class ErrorController extends AbstractErrorController{
@Autowired
ObjectMapper objectMapper;
public ErrorController() {
super(new DefaultErrorAttributes());
}
@Override
public String getErrorPath() {
return null;
}
@RequestMapping("/error")
@ResponseBody
public BaseRs getErrorPath(HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> model = Collections.unmodifiableMap(getErrorAttributes(request,false));
//获取异常 可将异常打印到日志
Throwable cause = getCause(request);
int status = (Integer)model.get("status");
//自定义友好错误信息
String msg = (String)model.get("message");
JSONObject object = JSONObject.parseObject(msg);
int code = object.getInteger("code");
String message = object.getString("message");
return new BaseRs(code,message);
}
protected Throwable getCause(HttpServletRequest request)
{
Throwable error = (Throwable)request.getAttribute("javax.servlet.error.exception");
if(null == error){
//MVC有可能会封装异常成ServletException ,需要调用getCause获取真正的异常
while (error instanceof ServletException && error.getCause() != null){
error = ((ServletException) error).getCause();
}
}
return error;
}
}
以上三个文件为异常处理的核心
其中的BaseRs类是统一数据返回
public class BaseRs<T> implements Serializable {
private int code;
private String message;
public BaseRs() {
}
/**
* 返回内容
*/
private T content;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public T getContent() {
return content;
}
public BaseRs(int code, String message) {
this.code = code;
this.message = message;
}
public BaseRs(int code, String message, T content) {
this.code = code;
this.message = message;
this.content = content;
}
public BaseRs(StatusCodeEnum status) {
this.code = status.getCode();
this.message = status.getMsg();
}
public BaseRs(StatusCodeEnum status, T content) {
this.code = status.getCode();
this.message = status.getMsg();
this.content = content;
}
public static <V> BaseRs ok(V content) {
return new BaseRs(StatusCodeEnum.SUCCESS, content);
}
public static BaseRs ok() {
return new BaseRs(StatusCodeEnum.SUCCESS);
}
public static BaseRs error(StatusCodeEnum error) {
return new BaseRs(error);
}
public void setCode(StatusCodeEnum status) {
this.code = status.getCode();
}
public void setMessage(String message) {
this.message = message;
}
public void setContent(T content) {
this.content = content;
}
@Override
public String toString() {
return "BaseRs{" +
"code=" + code +
", message='" + message + '\'' +
", content=" + content +
'}';
}
public void setCode(int code) {
this.code = code;
}
}
View Code
最后的控制层代码以最简洁的方式调用即可:
@Controller
public class IndexController {
@RequestMapping("/a")
@ResponseBody
public BaseRs a() throws Exception{
boolean s = false;
if(!s){
BaseException.error(StatusCodeEnum.USER_INVALID);
}
return new BaseRs(StatusCodeEnum.SUCCESS);
}
}
本篇博客的码云地址: https://gitee.com/zhao-baolin/springboot_error