apache dubbo 自定义全局统一的异常处理器
发布于: 2021 年 05 月 21 日
项目使用过的是 apache dubbo 2.7.1, 封装了自定义全局统一的异常处理器。
统一异常处理器
需要实现 javax.ws.rs.ext.ExceptionMapper 接口。
import org.apache.dubbo.rpc.RpcException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jboss.resteasy.spi.DefaultOptionsMethodException;
import org.springframework.util.StringUtils;
import javax.exceptions.ServiceException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/**
* 全局统一的异常处理器。
*/
public class CustomExceptionMapper implements ExceptionMapper<Exception> {
private final static Logger logger = LogManager.getLogger(CustomExceptionMapper.class);
private static final String TEXT_PLAIN_UTF_8 = "text/plain; charset=UTF-8";
private static final String JSON_UTF_8 = "application/json;charset=UTF-8";
private static final int FILTER_SIZE = 60;
private static final String DEV = "dev";
private static final String TEST = "test";
private static final int UNAUTHORIZED = 401;
public static String active;
public static void setActive(String at) {
active = at;
}
@Override
public Response toResponse(Exception e) {
if (e instanceof DefaultOptionsMethodException ||
(StringUtils.hasText(e.getMessage()) && e.getMessage().contains("org.jboss.resteasy.spi.DefaultOptionsMethodException"))) {
// 忽略此异常: Option跨域请求探测
return Response.status(Response.Status.OK).type(TEXT_PLAIN_UTF_8).build();
}
Response.Status httpStatus = Response.Status.INTERNAL_SERVER_ERROR;
Result<String> errorResult = null;
List<String> messages = new ArrayList<>();
if (e.getStackTrace().length > 0) {
StackTraceElement ste = e.getStackTrace()[0];
messages.add("异常内容:" + e.getMessage());
messages.add("异常名:" + e.getClass());
messages.add("异常类名:" + ste.getClassName());
messages.add("异常文件名:" + ste.getFileName());
messages.add("异常行号:" + ste.getLineNumber());
messages.add("异常方法:" + ste.getMethodName());
}
//1.处理404异常
if (e instanceof NotFoundException) {
logger.error("发生404异常: ", e);
errorResult = fail(404, "404 not found!");
httpStatus = Response.Status.NOT_FOUND;
}
//2.dubbo远程调用异常
if (e instanceof RpcException) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), "服务重新连线中,请稍后", messages);
httpStatus = Response.Status.UNAUTHORIZED;
}
//3.非法参数异常
if (e instanceof IllegalArgumentException) {
if (!StringUtils.isEmpty(e.getMessage())) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), filterChinese(e.getMessage()), messages);
} else {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), filterChinese("内部错误:A04"), messages);
}
}
//4.对象解析异常
if (e instanceof IllegalStateException) {
if (StringUtils.isEmpty(e.getMessage())) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), filterChinese("内部错误:A03"), messages);
} else {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), filterChinese(e.getMessage()), messages);
}
}
//5.强制转换异常
if (e instanceof ClassCastException) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), "内部错误:A01", messages);
}
//6.空指针
if (e instanceof NullPointerException) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), "内部错误:A02", messages);
}
//7.处理自定义异常
if (e instanceof AlertException) {
errorResult = fail(((AlertException) e).getErrorCode(), e.getMessage(), messages);
logger.error("提示信息:{}", e.getMessage());
} else if (e instanceof AlertAndReloadException) {
errorResult = fail(((AlertAndReloadException) e).getErrorCode(), e.getMessage(), messages);
logger.error("提示信息:{}", e.getMessage());
} else if (e instanceof ServiceException) {
ServiceException serviceException = (ServiceException) e;
//统一异常码
Integer errorCode = serviceException.getErrorCode();
String errorInfo = serviceException.getErrorInfo();
if ((!StringUtils.isEmpty(errorCode)) && errorCode == UNAUTHORIZED) {
httpStatus = Response.Status.UNAUTHORIZED;
}
if (StringUtils.isEmpty(errorCode)) {
errorCode = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
}
if (httpStatus == Response.Status.UNAUTHORIZED) {
logger.debug("发生自定义异常,异常码:{}, 异常说明:{}", errorCode, errorInfo);
} else {
logger.error("发生自定义异常,异常码:{}, 异常说明:{}", errorCode, errorInfo, e);
}
//错误提示信息
String reason = StringUtils.hasText(errorInfo) ? errorInfo : ErrorCodeEnums.getByCode(errorCode);
errorResult = fail(errorCode, StringUtils.hasText(reason) ? reason : errorInfo, messages);
}
//8.其他异常
if (errorResult == null) {
logger.error("发生异常: ", e);
String msg = e.getMessage();
if (StringUtils.isEmpty(msg) || msg.length() > FILTER_SIZE) {
msg = "服务器开小差了,请重试";
}
if (!StringUtils.isEmpty(active) && (active.equals(DEV) || active.equals(TEST))) {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), msg, getExceptionDetail(e), messages);
} else {
errorResult = fail(ErrorCodeEnums.InternalServerError.getCode(), msg, messages);
}
}
return Response.status(httpStatus).entity(errorResult.toString()).type(JSON_UTF_8).build();
}
private Result<String> fail(int code, String msg) {
return new Result<>(code, msg);
}
private Result<String> fail(int code, String msg, List<String> messages) {
return new Result<>(code, msg, null, messages);
}
private Result<String> fail(int code, String msg, String errMessage) {
return new Result<>(code, msg, errMessage);
}
private Result<String> fail(int code, String msg, String errMessage, List<String> messages) {
return new Result<>(code, msg, errMessage, messages);
}
/**
* 过滤掉 非中文字符。
*/
private static String filterChinese(final String str) {
if (!StringUtils.hasText(str)) {
return str;
}
if (str.length() <= FILTER_SIZE) {
return str;
}
//只保留前60个中文字符
String filterStr = str.replaceAll("[^(\\u4e00-\\u9fa5)]", "")
.replaceAll("\\(", "")
.replaceAll("\\)", "")
.replaceAll("\\.", "")
.replaceAll("\\_", "");
if (StringUtils.isEmpty(filterStr)) {
//过滤后为空,则原样显示前60个字符
return str.substring(0, FILTER_SIZE);
} else if (filterStr.length() > FILTER_SIZE) {
return filterStr.substring(0, FILTER_SIZE);
} else if (filterStr.length() < 15) {
return str;
} else {
return filterStr;
}
}
/**
* 获取异常详细信息
*/
public static String getExceptionDetail(Exception ex) {
String ret = null;
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream pout = new PrintStream(out);
ex.printStackTrace(pout);
ret = new String(out.toByteArray());
pout.close();
out.close();
} catch (Exception e) {
}
return ret;
}
}
复制代码
统一异常码枚举类 ErrorCodeEnums.java:
/**
* 全局统一的异常码。
*/
public enum ErrorCodeEnums {
// ----------------- http标准状态码---------------------------------
Unauthorized(401, "请先登录"),
Forbidden(403, "您无权限访问此页面,请联系管理员或重新登录"),
NotFound(404, "抱歉,您访问的页面不存在"),
InternalServerError(500, "服务器内部错误"),
//----------------- 自定义异常码 -----------------------------------
ServerErrorRetry(666, "服务器内部错误,前端页面尝试再次调用接口直到返回666"),
ParamsError(1001, "参数校验失败"),
TokenExpired(1002, "登录token已过期"),
AlertMsg(1003, "页面提示信息"),
AlertMsgAndReload(1004, "提示信息,并自动刷新页面"),
;
/**
* 异常码。
*/
public Integer code;
/**
* 异常描述。
*/
public String reason;
ErrorCodeEnums(Integer code, String reason) {
this.code = code;
this.reason = reason;
}
public static String getByCode(Integer code) {
for (ErrorCodeEnums item : ErrorCodeEnums.values()) {
if (code.equals(item.getCode())) {
return item.reason;
}
}
return "";
}
public Integer getCode() {
return code;
}
public String getReason() {
return reason;
}
}
复制代码
application.yml 文件配置
在 application.yml 文件中配置启用
dubbo:
application:
name: xxx-service
# 优雅停机超时时间
shutwait: 15000
provider:
#负载均衡
loadbalance: roundrobin
timeout: 10000
filter: xxxFilter
consumer: #关闭启动时检查依赖服务
check: false
filter: xxxFilter
protocols:
dubbo:
name: dubbo
port: -1
server: netty
extension: xxx.CustomExceptionMapper
rest:
name: rest
port: 80
server: tomcat
contextpath: xxx-service
extension: xxx.CustomExceptionMapper
复制代码
划线
评论
复制
发布于: 2021 年 05 月 21 日阅读数: 20
版权声明: 本文为 InfoQ 作者【try catch】的原创文章。
原文链接:【http://xie.infoq.cn/article/05bc860bbe6427190c53753b9】。文章转载请联系作者。
try catch
关注
还未添加个人签名 2012.07.23 加入
还未添加个人简介
评论