SpringCloud请求异常处理封装BusinessException自定义异常类
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud请求异常处理封装BusinessException自定义异常类相关的知识,希望对你有一定的参考价值。
文章目录
请求异常处理
1 异常信息格式
系统在交互中难免会有异常发生,前端为了解析异常信息向用户提示特定义了异常信息的返回格式,如下:
1、返回response状态说明
状态码 | 说明 |
---|---|
200 | 成功 |
401 | 没有权限 |
500 | 程序错误(需要自定义错误体) |
2、自定义错误体
"errCode": "你定义的错误码",
"errMessage": "错误说明"
例如下面根据不同的业务自定义的错误码:
2 异常处理流程
截至目前系统并没有按照前端要求返回异常信息,测试如下:
注册商户时输入一个错误的验证码,返回信息如下
"timestamp": "2021‐12‐10T10:06:19.936+0000",
"status": 500,
"error": "Internal Server Error",
"message": "验证码错误",
"path": "/merchant/merchants/register"
从上边的返回信息得知,状态码为500符合要求,按前端的规范定义的错误信息要写在“errMessage” 中,显然不符合要求。
系统规范了异常处理流程,如下:
1、在服务层抛出自定义异常类型及不可预知异常类型。
上图中BusinessException为系统的自定义异常类型,程序中在代码显示抛出该异常,此类异常是程序员可预知的。
另一部分是系统无法预知的异常,如:数据库无法连接,服务器宕机等场景下所抛出的异常,此类异常是程序员无法预知的异常。
2、应用层接收到服务层抛出异常继续向上抛出,应用层自己也可以抛出自定义异常类型及不可预知异常类型。
3、统一异常处理器捕获到异常进行解析。
判断如果为自定义异常则直接取出错误代码及错误信息,因为程序员在抛出自定义异常时已将错误代码和异常信息指定。
如果为不可预知的异常则统一定义为99999异常代码。
4、统一异常处理器将异常信息格式为前端要求的格式响应给前端。
服务端统一将异常信息封装在下边的Json格式中返回:
"errCode": "000000",
"errMessage": "错误说明"
3 自定义业务异常类
1、在shanjupay-common工程的com.shanjupay.common.domain包下添加业务异常类BusinessException:
package com.shanjupay.common.domain;
/**
* 自定义的异常类型
* @author Administrator
* @version 1.0
**/
public class BusinessException extends RuntimeException
private ErrorCode errorCode;
public BusinessException(ErrorCode errorCode)
super();
this.errorCode = errorCode;
public BusinessException()
super();
public void setErrorCode(ErrorCode errorCode)
this.errorCode = errorCode;
public ErrorCode getErrorCode()
return errorCode;
2、定义错误代码
在common工程专门定义了ErrorCode接口及CommonErrorCode通用代码。
编写 ErrorCode 接口
package com.shanjupay.common.domain;
public interface ErrorCode
int getCode();
String getDesc();
编写 CommonErrorCode通用代码
package com.shanjupay.common.domain;
/**
* 异常编码
*/
public enum CommonErrorCode implements ErrorCode
公用异常编码 //
E_100101(100101,"传入参数与接口不匹配"),
E_100102(100102,"验证码错误"),
E_100103(100103,"验证码为空"),
E_100104(100104,"查询结果为空"),
E_100105(100105,"ID格式不正确或超出Long存储范围"),
E_100106(100106,"上传错误"),
E_100107(100107,"发送验证码错误"),
E_100108(100108,"传入对象为空"),
E_100109(100109,"手机号格式不正确"),
E_100110(100110,"用户名为空"),
E_100111(100111,"密码为空"),
E_100112(100112,"手机号为空"),
E_100113(100113,"手机号已存在"),
E_100114(100114,"用户名已存在"),
E_100115(100115,"密码不正确"),
SAAS服务异常编码110 //
E_110001(110001,"账号不存在"),
E_110002(110002,"角色编码在同一租户中已存在,不可重复"),
E_110003(110003,"角色为空"),
E_110004(110004,"角色已绑定账号,被使用中不可删除"),
E_110005(110005,"权限集合为空"),
E_110006(110006,"参数为空"),
E_110007(110007,"未查询到租户关联的角色"),
E_110008(110008,"账号被其他租户使用,不可删除"),
商户服务异常编码200//
E_200001(200001,"企业名称不能为空"),
E_200002(200002,"商户不存在"),
E_200003(200003,"商户还未通过认证审核,不能创建应用"),
E_200004(200004,"应用名称已经存在,请使用其他名称"),
E_200005(200005,"应用不属于当前商户"),
E_200006(200006,"门店不属于当前商户"),
E_200007(200007,"二维码生成失败"),
E_200008(200008,"授权码为空"),
E_200009(200009,"订单标题为空"),
E_200010(200010,"订单金额为空"),
E_200011(200011,"授权码格式有误"),
E_200012(200012,"租户不存在"),
E_200013(200013,"员工不存在"),
E_200014(200014,"商户下未设置根门店"),
E_200015(200015,"未查询到该门店"),
E_200016(200016,"资质申请已通过,无需重复申请"),
E_200017(200017,"商户在当前租户下已经注册,不可重复注册"),
E_200018(200018,"商户下的根门店,不可删除"),
交易服务异常编码300//
E_300001(300001,"支付金额为空"),
E_300002(300002,"openId为空"),
E_300003(300003,"appId为空"),
E_300004(300004,"商户id为空"),
E_300005(300005,"服务类型编码为空"),
E_300006(300006,"订单金额转换异常"),
E_300007(300007,"原始支付渠道为空"),
E_300008(300008,"已存在相同的支付参数,不可重复配置"),
E_300009(300009,"传入对象为空或者缺少必要的参数"),
E_300010(300010,"应用没有绑定服务类型,不允许配置参数"),
E_300110(300110,"交易单号不能为空"),
支付渠道代理服务异常编码400//
E_400001(400001,"微信确认支付失败"),
E_400002(400002,"支付宝确认支付失败"),
运营服务异常编码500//
特殊异常编码/
E_999991(999991,"调用微服务-授权服务 被熔断"),
E_999992(999992,"调用微服务-用户服务 被熔断"),
E_999993(999993,"调用微服务-资源服务 被熔断"),
E_999994(999994,"调用微服务-同步服务 被熔断"),
E_999910(999910,"调用微服务-没有传tenantId租户Id"),
E_999911(999911,"调用微服务-没有json-token令牌"),
E_999912(999912,"调用微服务-json-token令牌解析有误"),
E_999913(999913,"调用微服务-json-token令牌有误-没有当前租户信息"),
E_999914(999914,"调用微服务-json-token令牌有误-该租户下没有权限信息"),
E_NO_AUTHORITY(999997,"没有访问权限"),
CUSTOM(999998,"自定义异常"),
/**
* 未知错误
*/
UNKNOWN(999999,"未知错误");
private int code;
private String desc;
public int getCode()
return code;
public String getDesc()
return desc;
private CommonErrorCode(int code, String desc)
this.code = code;
this.desc = desc;
public static CommonErrorCode setErrorCode(int code)
for (CommonErrorCode errorCode : CommonErrorCode.values())
if (errorCode.getCode()==code)
return errorCode;
return null;
目录结构如下:
4 自定义业务异常处理器
1、在shanjupay‐common工程的com.shanjupay.common.domain包下添加错误响应包装类RestErrorResponse:
package com.shanjupay.common.domain;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
* @author Administrator
* @version 1.0
**/
@ApiModel(value = "RestErrorResponse", description = "错误响应参数包装")
@Data
public class RestErrorResponse
private String errCode;
private String errMessage;
public RestErrorResponse(String errCode,String errMessage)
this.errCode = errCode;
this.errMessage= errMessage;
2、定义全局异常处理器
全局异常处理器使用ControllerAdvice注解实现,ControllerAdvice是SpringMVC3.2提供的注解,用 ControllerAdvice可以方便实现对Controller面向切面编程,具体用法如下:
注解 | 说明 |
---|---|
ControllerAdvice和ExceptionHandler注解 | 实现全局异常处理 |
ControllerAdvice和ModelAttribute注解 | 实现全局数据绑定 |
ControllerAdvice生InitBinder注解 | 实现全局数据预处理 |
这里用到的是第一种用法:ControllerAdvice和ExceptionHandler结合可以捕获Controller抛出的异常
ControllerAdvice和ExceptionHandler结合可以捕获Controller抛出的异常,根据异常处理流程,Service和持久层最终都会抛给Controller,所以此方案可以实现全局异常捕获,异常被捕获到即可格式为前端要的信息格式响应给前端。
在shanjupay‐merchant‐application工程 com.shanjupay.merchant.common.intercept添加
GlobalExceptionHandler:
package com.shanjupay.merchant.common.intercept;
import com.shanjupay.common.domain.BusinessException;
import com.shanjupay.common.domain.CommonErrorCode;
import com.shanjupay.common.domain.ErrorCode;
import com.shanjupay.common.domain.RestErrorResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理器
* @author Administrator
* @version 1.0
**/
@ControllerAdvice//与@Exceptionhandler配合使用实现全局异常处理:可以捕获Controller抛出的异常
public class GlobalExceptionHandler
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
//捕获Exception异常
@ExceptionHandler(value = Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse processExcetion( HttpServletRequest request,
HttpServletResponse response,
Exception e)
//解析异常信息
//如果是系统自定义异常,直接取出errCode和errMessage
if(e instanceof BusinessException)
LOGGER.info(e.getMessage(),e);
//解析系统自定义异常信息
BusinessException businessException= (BusinessException) e;
ErrorCode errorCode = businessException.getErrorCode();
//错误代码
int code = errorCode.getCode();
//错误信息
String desc = errorCode.getDesc();
return new RestErrorResponse(String.valueOf(code),desc);
LOGGER.error("系统异常:",e);
//统一定义为99999系统未知错误
return new RestErrorResponse(String.valueOf(CommonErrorCode.UNKNOWN.getCode()),CommonErrorCode.UNKNOWN.getDesc());
5 抛出自定义异常
按照异常处理流程,应用层抛出自定义异常由异常处理器进行解析。
1、校验验证码接口抛出 BusinessException
修改商户平台应用工程中SmsServicer的verifificationMessageCode接口
public void checkVerifyCode(String verifyKey, String verifyCode) throws BusinessException;
接口实现中抛出异常自定义异常类型
/**
* 校验手机验证码
*
* @param verifiyKey 验证码的key
* @param verifiyCode 验证码
*/
@Override
public void checkVerifiyCode(String verifiyKey, String verifiyCode) throws BusinessException
//校验验证码的url
String url = "http://localhost:56085/sailing/verify?name=sms&verificationCode="+verifiyCode+"&verificationKey="+verifiyKey;
Map bodyMap = null;
try
//使用restTemplate请求验证码服务
ResponseEntity<Map> exchange = restTemplate.exchange(url, HttpMethod.POST, HttpEntity.EMPTY, Map.class);
log.info("请求验证码服务,得到响应:", JSON.toJSONString(exchange));
bodyMap = exchange.getBody();
catch (Exception e)
e.printStackTrace();
throw new BusinessException(CommonErrorCode.E_100102);
// throw new RuntimeException("校验验证码失败");
if(bodyMap == null || bodyMap.get("result") == null || !(Boolean) bodyMap.get("result"))
throw new BusinessException(CommonErrorCode.E_100102);
2、测试
请求商户注册,输出一个错误的验证码,返回信息如下:
"errCode": "100102",
"errMessage": "验证码错误"
3、测试不可预知异常
故意在Controller中制造异常,测试是否抛出未知错误异常。
代码如下:
@PostMapping("/merchants/register")
public MerchantRegisterVO registerMerchant(@RequestBody MerchantRegisterVO merchantRegister)
int i=1/0;//故意制造异常
....
请商户注册,返回信息如下:
"errMessage": "未知错误",
"errCode": "999999"
以上是关于SpringCloud请求异常处理封装BusinessException自定义异常类的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解
SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解
企业分布式微服务云SpringCloud SpringBoot mybatis Spring Boot中Web应用的统一异常处理