博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用SpringCloud Alibaba搭建属于自己的微服务(十)~基础搭建~自定义异常、统一结果集和全局异常处理器
阅读量:4204 次
发布时间:2019-05-26

本文共 14462 字,大约阅读时间需要 48 分钟。

一.自定义异常体系

1.背景

在业务代码的开发中,往往会有这种情况,代码逻辑走到某一步,通过if的判断发现程序的逻辑无法继续往下面走了,否则会有一定的问题,这时候我们需要抛出异常组织程序往下进行,快速响应这个请求,并且回滚事物,这就是我们的自定义异常体系.

2.设计

如何设计自定义异常体系,需要我们对自身系统的业务有一定的了解,例如一个商城,可能会有以下的自定义异常.

异常 场景
OrderException.class 订单业务发生异常
PayException.class 支付业务发生异常
GoodsException.class 商品业务发生异常

我们这里不做如何详细的异常体系设计,暂时只区分客户端异常,服务端异常.

(1)代码
CcmMallException.java

package com.ccm.common.exception;/** *  @Description 自定义异常超类 *  @Author ccm *  @CreateTime 2020/07/10 14:35 */public class CcmMallException extends RuntimeException {
}

CustomerException .java

package com.ccm.common.exception;import lombok.Getter;/** * @Description 客户端异常 * @Author ccm * @CreateTime 2020/7/10 14:40 */@Getterpublic class CustomerException extends CcmMallException {
private CodeEnum codeEnum; //状态码 private String errorMessage; //错误详细信息 public CustomerException(String errorMessage) {
this.codeEnum = CodeEnum.ILLEGAL_REQUEST; this.errorMessage = errorMessage; } public CustomerException(CodeEnum codeEnum, String errorMessage) {
this.codeEnum = codeEnum; this.errorMessage = errorMessage; }}

ServerException.java

package com.ccm.common.exception;import lombok.Getter;/** *  @Description 服务端异常 *  @Author ccm *  @CreateTime 2020/07/10 14:43 */@Getterpublic class ServerException extends CcmMallException {
private CodeEnum codeEnum; //状态码 private String errorMessage; //错误详细信息 public ServerException(String errorMessage) {
this.codeEnum = CodeEnum.SYSTEM_INNER_ERROR; this.errorMessage = errorMessage; } public ServerException(CodeEnum codeEnum, String errorMessage) {
this.codeEnum = codeEnum; this.errorMessage = errorMessage; }}

二.统一结果集

1.common包里加入Reultset类,提供构造统一结果集的静态方法

package com.ccm.common.exception.result;import com.alibaba.fastjson.annotation.JSONField;import com.fasterxml.jackson.annotation.JsonFormat;import lombok.Getter;import lombok.Setter;import lombok.ToString;import lombok.extern.slf4j.Slf4j;import java.util.Date;/** * @Description 统一结果集 * @Author ccm * @CreateTime 2020/7/14 17:36 */@Slf4j@Getter@ToStringpublic class ResultSet
{
private Integer code; //状态码 private String codeMessage; //状态码信息 @Setter private String errorMessage; //前后端对接错误信息 @Setter private String errorMessageToUser; //给用户看的错误信息 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss:SSS",timezone = "GMT+8") //jackson指定时间转换格式 @JSONField(format="yyyy-MM-dd HH:mm:ss:SSS") //fastjson指定时间转换格式 private Date timestamp = new Date(); @Setter private T data; //响应数据 //私有构造器 private ResultSet() {
} //响应成功,无响应数据 public static ResultSet success() {
ResultSet resultSet = new ResultSet(); resultSet.code = CodeEnum.SUCCESS.getCode(); resultSet.codeMessage = CodeEnum.SUCCESS.getCodeMessage(); return resultSet; } //响应成功,有响应数据 public static
ResultSet
success(T data) {
ResultSet resultSet = new ResultSet(); resultSet.code = CodeEnum.SUCCESS.getCode(); resultSet.codeMessage = CodeEnum.SUCCESS.getCodeMessage(); resultSet.data = data; return resultSet; } //响应失败(仅供异常处理器使用,其他地方建议均以抛出异常的方式响应) public static ResultSet error(CodeEnum codeEnum, String errorMessage,String errorMessageToUser) {
ResultSet resultSet = new ResultSet(); resultSet.code = codeEnum.getCode(); resultSet.codeMessage = codeEnum.getCodeMessage(); resultSet.errorMessage = errorMessage; resultSet.setErrorMessageToUser(errorMessageToUser); return resultSet; }}

2.测试结果集

(1)server.pom中加入公共包的依赖

com.ccm
common
1.0.0

(2)server-user服务中编写测试代码

package com.ccm.server.user.controller;import com.alibaba.fastjson.JSONObject;import com.ccm.common.exception.result.ResultSet;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** *  @Description 结果集测试 *  @Author ccm *  @CreateTime 2020/07/14 17:42 */@RestController@RequestMapping(value = "resultTest")@Api(tags = "结果集,全局异常处理器,自定义异常测试")public class ResultTestController {
@ApiOperation(value = "结果集测试") @GetMapping public ResultSet
test01() {
JSONObject vo = new JSONObject(); vo.put("name","zhouzhiwu"); vo.put("age","10"); return ResultSet.success(vo); }}

(3)swagger调用

在这里插入图片描述

三.全局异常处理器

1.概述

(1)全局异常处理器的作用是捕获接口调用是产生的异常,在全局异常处理器中我们可以集中处理.

(2)我们定义了我们自己的结果集,所有的接口调用无论成功与否,http状态码都为200,具体的调用情况依据结果集中的code确定.

2.加入全局异常处理器

(1)为了让所有的微服务能够都使用全局异常处理器,我们将其定义在common包中.

(2)common.pom中加入依赖,主要作用是能够使用springbootweb相关的注解,编译不报错,所以依赖不传递.

org.springframework.boot
spring-boot-starter-web
true

(3)common中编写全局异常处理器代码,代码中注释完善,所以不做过多的解释.

package com.ccm.common.handler;import com.ccm.common.exception.CcmMallException;import com.ccm.common.exception.CustomerException;import com.ccm.common.exception.ServerException;import com.ccm.common.exception.result.CodeEnum;import com.ccm.common.exception.result.ResultSet;import lombok.extern.slf4j.Slf4j;import org.apache.catalina.servlet4preview.http.HttpServletRequest;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.converter.HttpMessageNotReadableException;import org.springframework.web.HttpMediaTypeNotAcceptableException;import org.springframework.web.HttpMediaTypeNotSupportedException;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.MissingPathVariableException;import org.springframework.web.bind.MissingServletRequestParameterException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;import org.springframework.web.servlet.NoHandlerFoundException;import javax.annotation.PostConstruct;import javax.validation.ConstraintViolationException;/** * @Description 全局异常处理器 * @Author ccm * @CreateTime 2020/7/15 10:13 */@Slf4j@RestControllerAdvicepublic class GlobalExceptionHandler {
/** * 服务名 */ @Value("${spring.application.name}") private String serverName; /** * 错误信息前缀 */ private String errorMessagePrefix; @PostConstruct public void init() {
this.errorMessagePrefix = new StringBuffer().append("(") .append(this.serverName) .append("服务") .append("->") .append(") ").toString(); } /** * @Description 处理系统内部异常(未知异常,入空指针,索引越界) * @Author ccm * @CreateTime 2020/7/15 10:13 * @Params [e, request] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
Exception.class}) public ResultSet handlerException(Exception e, HttpServletRequest request) {
log.error("请求路径uri={},系统内部出现异常:{}", request.getRequestURI(),e); ResultSet resultSet = ResultSet.error(CodeEnum.SYSTEM_INNER_ERROR,errorMessagePrefix+e.toString(),"服务器繁忙,请稍后再试"); return resultSet; } /** * @Description 非法请求异常(SpringAOP) * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [exception] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
HttpMediaTypeNotAcceptableException.class, HttpMediaTypeNotSupportedException.class, HttpRequestMethodNotSupportedException.class, MissingServletRequestParameterException.class, NoHandlerFoundException.class, MissingPathVariableException.class, HttpMessageNotReadableException.class }) public ResultSet handlerSpringAOPException(Exception exception) {
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+exception.getMessage(),exception.getMessage()); return resultSet; } /** * @Description 非法请求异常(@DateTimeFormat注解抛出异常) * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [e] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = MethodArgumentTypeMismatchException.class) public ResultSet handlerSpringAOPException(MethodArgumentTypeMismatchException e) {
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+e.getMessage(),e.getMessage()); return resultSet; } /** * @Description 非法请求(处理spring validation参数校验抛出异常1) * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [methodArgumentNotValidException] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
MethodArgumentNotValidException.class}) public ResultSet handlerMethodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException) {
//获取异常字段及对应的异常信息 StringBuffer stringBuffer = new StringBuffer(); methodArgumentNotValidException.getBindingResult().getFieldErrors().stream() .map(t -> t.getField()+"=>"+t.getDefaultMessage()+" ") .forEach(e -> stringBuffer.append(e)); String errorMessage = stringBuffer.toString(); ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+errorMessage,errorMessage); return resultSet; } /** * @Description 非法请求异常(处理spring validation参数校验抛出异常2) * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [constraintViolationException] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
ConstraintViolationException.class}) public ResultSet handlerConstraintViolationException(ConstraintViolationException constraintViolationException) {
String errorMessage = constraintViolationException.getLocalizedMessage(); ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST,errorMessagePrefix+errorMessage,errorMessage); return resultSet; } /** * @Description 处理自定义异常-CustomException * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [e] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
CustomerException.class}) public ResultSet handlerCustomException(CustomerException e) {
String errorMessage = e.getErrorMessage(); ResultSet resultSet = ResultSet.error(e.getCodeEnum(), errorMessagePrefix+errorMessage,errorMessage); return resultSet; } /** * @Description 处理自定义异常-ServerException * @Author ccm * @CreateTime 2020/7/15 10:14 * @Params [e] * @Return com.ccm.common.exception.result.ResultSet */ @ExceptionHandler(value = {
ServerException.class}) public ResultSet handlerServerException(ServerException e) {
String errorMessage = e.getErrorMessage(); ResultSet resultSet = ResultSet.error(e.getCodeEnum(), errorMessagePrefix+errorMessage,errorMessage); return resultSet; }}

(4)微服务中加入common包的依赖,server.pom中加入.

com.ccm
common
1.0.0
true

(5)微服务启动类上增加注解扫描的范围,保证能够扫描到全局异常处理器,启动类上加上@ComponentScan(basePackages = “com.ccm”).

package com.ccm.server.user;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Import;import springfox.documentation.swagger2.annotations.EnableSwagger2;/** *  @Description server-user服务启动类 *  @Author ccm *  @CreateTime 2020/07/03 14:04 */@ComponentScan(basePackages = "com.ccm")@EnableSwagger2@MapperScan(basePackages = "com.ccm.server.user.dao.mysql.mapper")@SpringBootApplication //声明为一个启动类@Import(value = PaginationInterceptor.class)public class ServerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ServerUserApplication.class,args); }}

3.全局异常处理器的测试代码见方法test02()、test()03和test04().

package com.ccm.server.user.controller;import com.alibaba.fastjson.JSONObject;import com.ccm.common.exception.CustomerException;import com.ccm.common.exception.result.ResultSet;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.hibernate.validator.constraints.Length;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;/** *  @Description 结果集测试 *  @Author ccm *  @CreateTime 2020/07/14 17:42 */@Validated@RestController@RequestMapping(value = "resultTest")@Api(tags = "结果集,全局异常处理器,自定义异常测试")public class ResultTestController {
@ApiOperation(value = "结果集测试") @GetMapping public ResultSet
test01() {
JSONObject vo = new JSONObject(); vo.put("name","zhouzhiwu"); vo.put("age","10"); return ResultSet.success(vo); } @ApiOperation(value = "全局处理器测试-未知异常") @GetMapping(value = "test02") public ResultSet
test02() {
JSONObject vo = new JSONObject(); vo.put("name","zhouzhiwu"); vo.put("age","10"); int i = 1/0; return ResultSet.success(vo); } @ApiOperation(value = "全局处理器测试-自定义异常") @GetMapping(value = "test03") public ResultSet
test03() {
throw new CustomerException("自定义异常"); } @ApiOperation(value = "全局处理器测试-参数校验框架抛出") @GetMapping(value = "test04") public ResultSet test04(@Length(min = 1,max = 2) @RequestParam String name) {
return ResultSet.success(name); }}

4.用swagger测试.

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

源码地址:

您的点赞、收藏、转发和关注是我持续创作的动力!

转载地址:http://yktli.baihongyu.com/

你可能感兴趣的文章