apache dubbo 自定义全局统一的异常处理器

项目使用过的是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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值