在开发中,关于异常的捕获曾经是一个头疼的问题;本篇介绍几个方法,如何优雅的捕获处理业务异常;

已检查异常和未检查异常?

先做个介绍,异常Exception分为运行时异常(RuntimeException)和非运行时异常,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)

(1) 可查异常checkedException(编译器要求必须处置的异常):正确的程序在运行中,很容易出现的、情理可容的异常状况。不管会不会一定出现,编译器都会强制你去Throws或者用try/catch;除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。

(2) 不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error),需要自己去抛出/捕获/处理,或者尽量避免。(推荐预测异常并处理异常)

如果使用throw在方法体中抛出可查异常,则需要在方法头部声明方法可能抛出throws的异常类型。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。

运行时异常和非运行时异常?

(1) 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。当出现RuntimeException的时候,我们可以不处理。当出现这样的异常时,总是由虚拟机接管。比如:我们从来没有人去处理过NullPointerException异常,它就是运行时异常,并且这种异常还是最常见的异常之一。

如果不想终止,则必须捕获所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理

(2) 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。如IOException、SQLException等以及用户自定义的Exception异常。对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。

所以——到底是应该是抛出异常,还是捕获/处理异常?

异常处理原则之一:延迟捕获,意思是,当异常发生时,不应立即捕获,而是应该考虑当前作用域是否有有能力处理这一异常的能力,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理。

例如:某方法String readFile(Stringfilename),会去尝试读出指定文件的内容并返回,其使用FileInputStream来读取指定文件,而FileInputStream的构造方法会抛出FileNotFoundException,这是一个CheckedException。那么readFile方法是应该捕获这个异常,还是抛出这个异常呢?很显然应该抛出。因为readFile这个方法可能会在不同的场景下,被不同的代码调用,在这些场景中,出现“文件未找到”的情况时的处理逻辑可能是不同的,例如某场景下要发出告警信息,另一场景下可能会尝试从另一个文件中读取,第三个场景下可能需要将错误信息提示给用户。在这种情况下,在readFile方法内的作用域中,是处理不了这个异常的,需要抛出,交由上层的,具备了处理这个异常的能力的作用域来处理;

总结下来,异常捕获要注意3个点

1. 绝对不能把异常信息抛给用户!对用户需要做成友好的用户提示语,对前端开发者可以给出一些提示方便交互和处理,也可以用来日志和埋点上报;

2. 异常捕获应该考虑当前作用域是否有有能力处理这一异常的能力,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理;

3. 业务异常应该是运行时异常,他应该与一个业务状态码枚举绑定,针对不同的状态码可以有不同的处理、提示方式;

这里介绍两种常用的业务异常捕获及处理的方案,可用于DUBBO或者HTTP接口的返回处理:

业务异常的定义

注意:如果是C端,业务异常除了message属性,可以加上toast属性

import AA.common.enums.ResultCodeEnum;
import lombok.Getter;/*** @author AA* @description 自定义的业务异常,可封装系统状态码、业务错误信息、异常原因等信息*/
@Getter
public class BusinessException extends RuntimeException {/*** 存放业务错误对应的系统返回码*/private ResultCodeEnum code;/*** 存放业务错误信息,且只在日志中打印*/private String busErrMsg;/*** 无业务错误信息返回的业务异常** @param code*/public BusinessException(ResultCodeEnum code) {super(code.getDesc());this.code = code;}/*** 有返回业务错误信息的业务异常** @param code* @param busErrMsg*/public BusinessException(ResultCodeEnum code, String busErrMsg) {super(code.getDesc());this.code = code;this.busErrMsg = busErrMsg;}/*** 封装指定异常类的业务异常** @param code* @param cause*/public BusinessException(ResultCodeEnum code, Throwable cause) {super(code.getDesc(), cause);this.code = code;}}

状态码枚举:

import lombok.AllArgsConstructor;
import lombok.Getter;/*** @author AA* @description 接口请求的返回状态的状态码枚举* 一级返回码:系统级别* 二级返回码:业务级别* @date 2019/11/27*/
@Getter
@AllArgsConstructor
public enum ResultCodeEnum {/*** 一级返回码,操作成功*/SUCCESS(0, "返回成功"),/*** 一级返回码,系统内部异常*/SERVER_ERROR(10000, "服务器内部错误(未知错误)"),/*** 一级返回码,系统请求第三方系统(如dubbo服务)异常*/REQUEST_THIRD_SYSTEM_EXCEPTION(10001, "系统请求第三方系统异常"),/*** 一级返回码,服务器繁忙*/SERVER_BUSYNESS(10002, "服务器繁忙"),/*** 一级返回码,请求方法Request.Method不支持*/REQUEST_METHOD_NOT_SUPPORT(10003, "请求方法不支持"),/*** 一级返回码,协议不支持*/PROTOCOL_NOT_SUPPORT(10004, "协议不支持"),/*** 一级返回码,系统未知错误*/UNKNOWN(10010, "系统未知错误"),/*** 二级返回码,用户未登录*/UN_LOGIN(20000, "用户未登录!"),/*** 二级返回码,错误参数*/BAD_PARAMS(20001, "请求参数错误"),/*** 二级返回码,HttpClient请求失败*/HTTPCLIENT_REQUEST_FAILED(20002, "HttpClient 请求失败!"),/*** 二级返回码,发生熔断*/CIRCUIT_BREAKERS(20003, "发生熔断"),/*** 业务返回码,礼券发送失败*/SENT_TICKET_FAIL(30001, "礼券发送失败"),/*** 业务返回码,领取失败*/RECEIVE_FAIL(30004, "领取失败"),/*** 业务返回码,积分发放失败*/SENT_POINT_FAIL(30005, "积分发放失败"),/*** 业务返回码,查询数据为空*/QUERY_DATA_EMPTY(30007, "查询数据为空"),;/*** 状态码*/private int code;/*** 描述*/private String desc;/*** 根据状态码找枚举类型** @param code* @return*/public static ResultCodeEnum byCode(int code) {for (ResultCodeEnum resultEnum : ResultCodeEnum.values()) {if (resultEnum.getCode() == code) {return resultEnum;}}throw new IllegalArgumentException("cannot find resultCode by error code : " + code);}
}

返回结果的定义

dubbo:FacadeResult

import XXcommon.enums.FacadeResultCodeEnum;
import lombok.Data;import java.io.Serializable;/*** @author AA* @description 默认的dubbo接口的返回DTO* @date 2019/11/27*/
@Data
public class FacadeDefaultDTO<T> implements Serializable {private static final long serialVersionUID = 6804764310822280735L;/*** 系统返回码*/private Integer code;/*** 返回码的信息*/private String msg;/*** 返回数据*/private T data;public FacadeDefaultDTO() {}private FacadeDefaultDTO(ResponseBuilder<T> builder) {this.code = builder.code;this.msg = builder.msg;this.data = builder.data;}public static ResponseBuilder builder() {return new ResponseBuilder();}public static class ResponseBuilder<T> {private int code = FacadeResultCodeEnum.SUCCESS.getCode();private String msg = FacadeResultCodeEnum.SUCCESS.getDesc();private T data;public ResponseBuilder code(int code) {this.code = code;return this;}public ResponseBuilder msg(String msg) {this.msg = msg;return this;}public ResponseBuilder data(T data) {this.data = data;return this;}public FacadeDefaultDTO<T> build() {return new FacadeDefaultDTO<>(this);}}/*** 返回成功,封装Data,编译器会泛型检查** @param data* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> FacadeDefaultDTO<T> success(T data) {return FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.SUCCESS.getCode()).msg(FacadeResultCodeEnum.SUCCESS.getDesc()).data(data).build();}/*** 返回失败,封装失败的返回码,data为null** @param facadeResultCodeEnum* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> FacadeDefaultDTO<T> fail(FacadeResultCodeEnum facadeResultCodeEnum) {return FacadeDefaultDTO.builder().code(facadeResultCodeEnum.getCode()).msg(facadeResultCodeEnum.getDesc()).data(null).build();}/*** 返回失败,自定义失败的返回码code和msg,data为null(用于业务异常码和服务异常码的转换)** @param errCode* @param errMsg* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> FacadeDefaultDTO<T> fail(int errCode, String errMsg) {return FacadeDefaultDTO.builder().code(errCode).msg(errMsg).data(null).build();}public boolean isSuccess() {return FacadeResultCodeEnum.SUCCESS.getCode() == this.getCode();}}

http:BaseResponse

import xx.common.enums.ResultCodeEnum;
import lombok.Data;/*** @author AA* @description 默认的controller的返回类* @date 2019/11/27*/
@Data
public class DefaultResponseDTO<T> {/*** 系统返回码*/private Integer code;/*** 返回码的信息*/private String msg;/*** 返回数据*/private T data;public DefaultResponseDTO() {}private DefaultResponseDTO(ResponseBuilder<T> builder) {this.code = builder.code;this.msg = builder.msg;this.data = builder.data;}public static ResponseBuilder builder() {return new ResponseBuilder();}public static class ResponseBuilder<T> {private int code = ResultCodeEnum.SUCCESS.getCode();private String msg = ResultCodeEnum.SUCCESS.getDesc();private T data;public ResponseBuilder code(int code) {this.code = code;return this;}public ResponseBuilder msg(String msg) {this.msg = msg;return this;}public ResponseBuilder data(T data) {this.data = data;return this;}public DefaultResponseDTO<T> build() {return new DefaultResponseDTO<>(this);}}/*** 返回成功,封装Data,编译器会泛型检查** @param data* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> DefaultResponseDTO<T> success(T data) {return DefaultResponseDTO.builder().code(ResultCodeEnum.SUCCESS.getCode()).msg(ResultCodeEnum.SUCCESS.getDesc()).data(data).build();}/*** 返回失败,封装失败的返回码** @param resultCodeEnum* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> DefaultResponseDTO<T> fail(ResultCodeEnum resultCodeEnum) {return DefaultResponseDTO.builder().code(resultCodeEnum.getCode()).msg(resultCodeEnum.getDesc()).build();}/*** 返回失败,用errMsg替换错误码的desc** @param resultCodeEnum* @param errMsg* @return*/@SuppressWarnings("unchecked")public static DefaultResponseDTO fail(ResultCodeEnum resultCodeEnum, String errMsg) {return DefaultResponseDTO.builder().code(resultCodeEnum.getCode()).msg(errMsg).data(null).build();}/*** 返回失败,封装失败的返回码和异常err_data** @param resultCodeEnum* @param err_data* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> DefaultResponseDTO<T> fail(ResultCodeEnum resultCodeEnum, T err_data) {return DefaultResponseDTO.builder().code(resultCodeEnum.getCode()).msg(resultCodeEnum.getDesc()).data(err_data).build();}public boolean isSuccess() {return ResultCodeEnum.SUCCESS.getCode() == this.getCode();}}

方式一:Controller的全局异常捕获器

import com.alibaba.csp.sentinel.slots.block.BlockException;
import XXcommon.enums.ResultCodeEnum;
import XXcommon.exception.BusinessException;
import XXcommon.model.DefaultResponseDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
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.bind.annotation.ResponseStatus;import javax.validation.ConstraintViolationException;
import java.util.Optional;/*** @author AA* @description 应用层全局异常处理类*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {private static String CLIENT_ABORT_EXCEPTION_PACKAGE_NAME = "org.apache.catalina.connector.ClientAbortException";@ExceptionHandler(Exception.class)@ResponseBodypublic DefaultResponseDTO handleException(Exception e) {//接口限流异常处理if (e != null && e.getCause() instanceof BlockException) {log.warn("[exception handler]BlockException occur! cause:{}", e.getCause() != null ? e.getCause() : "");return DefaultResponseDTO.fail(ResultCodeEnum.RATE_LIMIT);}//异常捕获的太全了,用户主动断开连接时服务端会产生ClientAbortException,该异常需要忽略if (e != null && CLIENT_ABORT_EXCEPTION_PACKAGE_NAME.equals(e.getClass().getName())) {log.error("[exception handler]ClientAbortException occur! ignore this exception.");return null;}log.error("[exception handler]controllerAdvice handleException, e:{}", e);return DefaultResponseDTO.fail(ResultCodeEnum.SERVER_BUSYNESS);}@ResponseStatus(HttpStatus.OK)@ExceptionHandler(BusinessException.class)@ResponseBodypublic DefaultResponseDTO handleBusinessException(BusinessException e) {String busErrMsg = e.getBusErrMsg();Throwable cause = e.getCause();if (null != cause) {log.error("[exception handler]handle BusinessException with Throwable=[{}]", cause);} else {String errorMessage = String.format("%s %s", e.getCode(), e.getMessage());log.error("[exception handler]handle BusinessException advice, errorMessage=[{}]", errorMessage);}return DefaultResponseDTO.fail(e.getCode(), StringUtils.isNotBlank(busErrMsg) ? busErrMsg : e.getMessage());}@ResponseStatus(HttpStatus.OK)@ExceptionHandler(BindException.class)@ResponseBodypublic DefaultResponseDTO handleValidException(BindException e) {StringBuilder builder = new StringBuilder();builder.append("bad params: ");Optional.ofNullable(e.getFieldErrors()).ifPresent(fieldErrors -> fieldErrors.forEach(f -> {builder.append(f.getField());builder.append(f.getDefaultMessage());builder.append("|");}));String message = removeLastSeparation(builder);log.error("[exception handler]handle ValidException, bad param=[{}]", message);return DefaultResponseDTO.fail(ResultCodeEnum.BAD_PARAMS, message);}@ResponseStatus(HttpStatus.OK)@ExceptionHandler(ConstraintViolationException.class)@ResponseBodypublic DefaultResponseDTO handleConstraintViolationException(ConstraintViolationException e) {StringBuilder builder = new StringBuilder();builder.append("bad params: ");Optional.ofNullable(e.getConstraintViolations()).ifPresent(constraintViolationSet -> constraintViolationSet.forEach(c -> {builder.append(c.getPropertyPath().toString());builder.append(StringUtils.isNotEmpty(c.getMessage()) ? " " + c.getMessage() : null);builder.append("|");}));String message = removeLastSeparation(builder);log.error("[exception handler]handle ConstraintViolationException, param=[{}]", message);return DefaultResponseDTO.fail(ResultCodeEnum.BAD_PARAMS, message);}private String removeLastSeparation(StringBuilder builder) {String message;if (null != builder && builder.length() > 0) {message = builder.substring(0, builder.length() - 1);} else {message = "";}return message;}}

方式二:DubboFilter

SPI方式:

dubboBizExceptionFilter=com.xx.filter.DubboBizExceptionFilter
package com.AA.service.helper.filter;import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xx.common.exception.BusinessException;
import com.xx.facade.common.enums.FacadeResultCodeEnum;
import com.xx.facade.common.model.FacadeDefaultDTO;
import org.apache.dubbo.rpc.service.GenericService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.validation.BindException;import javax.validation.ConstraintViolationException;
import java.util.Optional;/*** @author AA* @description dubbo异常拦截器*/
@Slf4j
@Activate(group = CommonConstants.PROVIDER)
public class DubboBizExceptionFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {try {org.apache.dubbo.rpc.Result result = invoker.invoke(invocation);if (result.hasException() && GenericService.class != invoker.getInterface()) {try {Throwable exception = result.getException();// 限流异常if (exception.getCause() != null && exception.getCause() instanceof BlockException) {log.warn("[DubboFilter]BlockException_occurred. [serviceName={} methodName={} args={}]", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()));AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.RATE_LIMITED.getCode()).msg(FacadeResultCodeEnum.RATE_LIMITED.getDesc()).build());return rpcResult;}// 业务异常 需注意: 不满足条件 被RuntimeException包裹时 需要特殊处理if (exception instanceof BusinessException) {log.warn("[DubboFilter]BusinessException_occurred. [serviceName={} methodName={} args={}]", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()));BusinessException busException = (BusinessException) exception;AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(busException.getCode().getCode()).msg(busException.getMessage()).build());return rpcResult;}// 参数validate     if (exception instanceof ConstraintViolationException) {ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;StringBuilder builder = new StringBuilder();Optional.ofNullable(constraintViolationException.getConstraintViolations()).ifPresent(constraintViolationSet -> {constraintViolationSet.forEach(c -> {log.warn(String.format("Call %s bad param: %s", c.getPropertyPath(), c.getMessage()));builder.append(c.getMessage());builder.append(" | ");});});String errorMessage = removeLastSeparation(builder);log.warn("[DubboFilter]ConstraintViolationException_occurred. [serviceName={} methodName={} args={} errorMessage={}]", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), errorMessage);AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.BAD_PARAMS.getCode()).msg(errorMessage).build());return rpcResult;}// spring validation参数异常if (exception instanceof BindException) {BindException bindException = (BindException) exception;StringBuilder builder = new StringBuilder();Optional.ofNullable(bindException.getFieldErrors()).ifPresent(fieldErrors -> {fieldErrors.forEach(f -> builder.append(f.getField()).append("|"));builder.deleteCharAt(builder.length() - 1);});String errorMessage = removeLastSeparation(builder);log.warn("[DubboFilter]BindException_occurred. [serviceName={} methodName={} args={} errorMessage={}]", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), errorMessage);AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.BAD_PARAMS.getCode()).msg(errorMessage).build());return rpcResult;}// 否则,RPC异常包装成本地BizException抛给客户端if (!(exception instanceof org.apache.dubbo.rpc.RpcException)) {log.error("[DubboFilter]OtherException_occurred. [serviceName={} methodName={} args={}] e:{}", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), exception);AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.SERVER_ERROR.getCode()).build());return rpcResult;} else {// 是Dubbo本身的异常,直接抛出log.error("[DubboFilter]RPCException_occurred. [serviceName={} methodName={} args={} e:{}]", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), exception);return result;}} catch (Throwable e) {log.error("[DubboFilter]Fail_to_ExceptionFilter_when_called_by: [serviceName={} methodName={} args={} exception={}] e:{}", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), e.getClass().getName(), e);return result;}}return result;} catch (RuntimeException e) {log.error("[DubboFilter]Got_exception_which_called_by: [serviceName={} methodName={} args={} exception={}] e:{}", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()), e.getClass().getName(), e);AsyncRpcResult rpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation);rpcResult.setValue(FacadeDefaultDTO.builder().code(FacadeResultCodeEnum.SERVER_ERROR.getCode()).build());return rpcResult;}}private String removeLastSeparation(StringBuilder builder) {String message;if (null != builder && builder.length() > 0) {message = builder.toString().substring(0, builder.length() - 2);} else {message = "";}return message;}}

注意,使用时可能出现:" 工程引入dubboFilter时,发现自定义业务异常不能被filter捕获,被包裹在运行时异常中 "

分析源码:org.apache.dubbo.rpc.filter.ExceptionFilter

做法:将Dubbo业务异常放在二方jar包中(这也是中间件二方包的常用做法);

方式三:切面捕获

这种方式较为灵活,精度可以到方法,但是要注意切面失效的一些情况,如同类调用;

注解:

package com.AA.web.helper.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author AA* @description API通用处理注解* @date 2022/3/10*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResultHandler {}

切面:

package com.AA.web.helper;import com.AA.dto.BaseResponse;
import com.AA.enums.ResultCodeEnum;
import com.AA.exception.BusinessException;
import com.AA.ConfigManager;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** @author AA* @description API通用处理* @date 2022/3/10*/
@Aspect
@Component
public class ResultHandlerAspect {private final static Logger LOGGER = LoggerFactory.getLogger(ResultHandlerAspect.class);private static final String GLOBAL_ERROR_LOG_SWITCH = "global.error.log.switch";@Around("@annotation(com.AA.helper.annotation.ResultHandler)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {Object proceed;try {proceed = joinPoint.proceed();}// 参数异常catch (IllegalArgumentException ie) {return BaseResponse.buildResult(ResultCodeEnum.PARAMS_ERROR);}// 业务异常catch (BusinessException e) {final ResultCodeEnum code = e.getCode();final String busErrMsg = e.getBusErrMsg();final String toast = e.getToast();if (code != null) {if (StringUtils.isNotBlank(busErrMsg)) {return BaseResponse.buildResult(code, String.join(" detail: ", code.getMessage(), busErrMsg), null);}return BaseResponse.buildResult(code);}// 无状态码则返回默认的系统异常return BaseResponse.buildResult(ResultCodeEnum.SYSTEM_ERROR);}// 其他异常封装 "系统异常"catch (Throwable e) {if (ConfigManager.getBoolean(GLOBAL_ERROR_LOG_SWITCH, true)) {LOGGER.error("unhandled_exception:", e);}return BaseResponse.buildResult(ResultCodeEnum.SYSTEM_ERROR);}return proceed;}}

编码技巧——全局异常捕获统一的返回体业务异常相关推荐

  1. ASP.NET Core搭建多层网站架构【11-WebApi统一处理返回值、异常】

    ASP.NET Core搭建多层网站架构[11-WebApi统一处理返回值.异常] 参考文章: (1)ASP.NET Core搭建多层网站架构[11-WebApi统一处理返回值.异常] (2)http ...

  2. ssm 异常捕获 统一处理_SpringMVC 统一异常处理介绍及实战

    背景 什么是统一异常处理 目标 统一异常处理实战 用 Assert(断言) 替换 throw exception 定义统一异常处理器类 扩展 总结 <Java 2019 超神之路> < ...

  3. ssm 异常捕获 统一处理_统一异常处理介绍及实战

    作者:sprinkle_lizhttp://www.jianshu.com/p/3f3d9e8d1efa 推荐阅读(点击即可跳转阅读) 1. SpringBoot内容聚合 2. 面试题内容聚合 3. ...

  4. ssm 异常捕获 统一处理_统一异常处理介绍及实战,看这篇就对了

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 每天凌晨00点00分, 第一时间与你相约 每日英文 Don't let anyone steal your dreams. Fo ...

  5. 基于springboot整合了mybatis plus,lombok,log4j2并实现了全局异常处理及统一数据返回格式(code,msg,data)

    1. 背景 由于微服务的流行,我们会动不动就建立一个新的项目作为一个服务,那么项目中的全局异常处理和统一数据格式是很重要的,如果设计不好,不仅开发时很乱,在查询日志时也会相当麻烦,所以我自己设计了一个 ...

  6. asp.net core webapi 统一处理返回值、异常和请求参数验证

    现在的开发模式很少用asp.net mvc一个项目直接操作界面和数据库了.大部分都使用前后端分离,更多的是为了让API支持移动端. 后端写webapi的时候必然需要和前端约定请求值和返回值的格式,如果 ...

  7. 业务异常 java_浅谈RxJava处理业务异常的几种方式

    本文介绍了RxJava处理业务异常的几种方式,分享给大家.具体如下: 关于异常 Java的异常可以分为两种:运行时异常和检查性异常. 运行时异常: RuntimeException类及其子类都被称为运 ...

  8. ssm 异常捕获 统一处理_SSM 统一异常处理

    SSM 统一异常处理 spring创建中, 处理异常可以使用try-cache处理, 也可以使用spring提供的统一异常处理 在spring中, 统一处理异常有2中方式 注解方式 @Exceptio ...

  9. 《SpringBoot从菜鸟到老鸟》之SpringBoot 如何配置全局的异常捕获

    SpringBoot 如何配置全局的异常捕获 SpringBoot中自带的异常捕获机制返回的默认页面比较丑,对用户来说不够人性化. 所以这篇文章来讲解SpringBoot钟自定义全局异常捕获. 主要讲 ...

最新文章

  1. C/C++中constkeyword
  2. php渲染nodejs api,如何使用nodejs 服务器读取HTML文件渲染至前端
  3. 用GDB调试程序(四)
  4. ML与Information:机器学习与Information信息论之间那些七七八八、乱七八糟、剪不断理还乱的关系攻略
  5. 【线上分享】短视频出海 — 用户体验衡量关键指标与优化策略
  6. Android中的AsyncTask异步任务的简单介绍
  7. 这本 “写不完” 的黑科技笔记本,恐怕要颠覆整个行业!
  8. 机器学习入门必备的13张“小抄”(附下载)
  9. java 内存 四_java最终化的内存保留问题(4)
  10. 攻击者接管账户,攻陷周下载量超700万次的JavaScript 流行库 ua-parser-js
  11. bzoj 1630 2023: [Usaco2005 Nov]Ant Counting 数蚂蚁(有重复元素的组合数)
  12. RDD之一:总体介绍
  13. SSH Secure Shell Client安装和使用
  14. 用 maven 命令启动项目和直接用tomcat 启动项目的区别
  15. 计算机恢复数据怎么恢复,电脑数据恢复,详细教您电脑数据如何恢复
  16. 一纬度横直线等于多公里_【地理】高中地理必修一知识点总结,考前必看
  17. 最全面贴片电阻知识,封装、尺寸、最大承受功率及选型统统包括-电子技术方案|电路图讲解
  18. 32位/64位处理器:*char与*int的区别?不同类型的指针+1的区别?
  19. Vue 大量数据展示卡顿解决方案(长列表优化)
  20. Oracle中打印99乘法表的13种方法

热门文章

  1. android改微信号码,安卓版微信更新,已支持修改微信号
  2. 我与谷歌共成长--谷歌常用的技巧#我和 Google 谷歌共成长
  3. mac删除ssh key_好用的mac终端命令仿真工具
  4. 关于使用手机电池替换3节干电池的尝试
  5. python的tkinter插入图片_Python3 Tkinter基础 Text Photoimage 文本框中插入一张图片
  6. 汉白玉产地在哪里_汉白玉产地在哪里?
  7. 浅析小程序云原生数据库的设计与应用
  8. 【Java】算法积累1——大整数相加
  9. MT6575芯片原理图MT6575原理图及量产板
  10. Tree | 伸展树