Bootstrap

Java自定义枚举状态码、全局异常处理

1、服务端返回给客户端的信息

1.1 自定义枚举状态码

HTTP定义了很多状态码,比如200表示成功,404表示资源未找到,使用HTTP状态码虽然能够大致表示请求的处理结果,但是无法精确的表示某些特定的业务情况。

1.1.1 自定义状态码优点
  • 状态回馈更加精准

    • 自定义状态码可以针对性地表示某种业务的特定情况,更加精准地反映请求的处理结果

  • 处理错误更加方便

    • 开发者可以针对每个状态码特定的处理,以便于更好地处理不同的错误情况

  • 易于扩展

    • 自定义状态码能够更好地应对业务的变化,可以根据业务需要自由地添加、移除或修改状态码来适应新的业务场景

1.1.2 自定义枚举状态码设计

电商项目

  • 用户模块 (UserController)

    • 请求URL设计: /v1/users/...

    • 自定义状态码设计: 10100 ~ 10199

  • 商品模块 (GoodsController)

    • 请求URL设计: /v1/goods/...

    • 自定义状态码设计: 10200 ~ 10299

  • 购物车模块 (CartsController)

    • 请求URL设计: /v1/carts/...

    • 自定义状态码设计: 10300 ~ 10399

  • 订单模块 (OrdersController)

    • 请求URL设计: /v1/goods/...

    • 自定义状态码设计: 10400 ~ 10499

  • 支付模块 (PaysController)

    • 请求URL设计: /v1/pays/...

    • 自定义状态码设计: 10500 ~ 10599

1.2 统一响应结果返回
1.2.1 优点
  • 简化前端开发

    • 项目开发中,前端需要与后端进行数据交互,通过统一响应结果返回,前端不需要每次对不同格式的响应结果进行解析,只需处理统一的数据结构。

  • 提高代码可读性和可维护性

    • 通过统一响应结果返回,可以将响应结果的处理逻辑集中在一个地方,提高代码的可读性和可维护性。

  • 统一处理状态码

    • 统一响应结果返回中可以定义不同的状态码来标识不同的响应结果,如成功、失败等。通过统一的状态码,可以快速判断响应结果的状态,方便进行后续的逻辑处理。

  • 统一处理异常

    • 在Spring MVC中,异常处理常常需要自定义异常处理方法,并在异常处理器中进行统一处理。通过统一响应结果返回,可以将异常信息统一封装成一个固定格式的数据结构返回给前端,方便异常的处理和展示。

1.2.2 编码流程

前提:一定是完成了自定义状态码的整体设计.

  • 第1步:自定义枚举类 common.response.StatusCode**;**

    • package cn.gongxi.weibo.common.response;
      ​
      import lombok.AllArgsConstructor;
      import lombok.Getter;
      import lombok.NoArgsConstructor;
      ​
      /**
       * 自定义枚举类状态码
       */
      @Getter
      @AllArgsConstructor   // 全参构造
      @NoArgsConstructor   // 无参构造
      ​
      public enum StatusCode{
          // 枚举实例
          OPERATION_SUCCESS(200,"响应成功");
          
          private Integer state;
          private String message;
      }

    示例:

    image-20240716194451391

  • 第2步:创建统一响应结果返回的类 common.response.JsonResult**;**

    • package cn.gongxi.weibo.common.response;
      ​
      import lombok.AllArgsConstructor;
      import lombok.Getter;
      import lombok.NoArgsConstructor;
      import lombok.Setter;
      ​
      /**
       * 统一响应结果的类
       */
      @Setter
      @Getter
      @AllArgsConstructor
      @NoArgsConstructor
      public class JsonResult {
          //状态码,提示消息,具体数据
          private Integer state;
          private String message;
          private Object data;
      ​
          /**
           * 1.构造方法:针对于没有具体数据返回的控制器方法[登录、注册、发微博]
           */
          public JsonResult(StatusCode statuscode){
              this.state = statuscode.getState();
              this.message = statuscode.getMessage();
          }
      ​
          /**
           * 2.构造方法:针对于有具体数据返回的控制器方法[微博首页、微博详情页、评论列表]
           */
          public JsonResult(StatusCode statuscode, Object data){
              this.state = statuscode.getState();
              this.message = statuscode.getMessage();
              this.data = data;
          }
          
          /**
           * 以下2个静态方法均是针对于操作成功的场景,比如:登录成功、注册成功、发微博成功......
           * 1.静态方法1:针对有具体数据返回的控制器方法;
           * 2.静态方法2:针对于没有具体数据返回的控制器方法;
           */
          
          public static JsonResult success(Object data){
              return new StatusCode(statuscode.OPERATION_SUCCESS,data);
          }
          
          publci static JsonResult success(){
              return success(null);
          }
      ​
      }

    示例:

    image-20240716194629778

  • 第3步:在 Controller 中使用.

    • public JsonResult login(){    
          return JsonResult.success();
      }

2、全局异常处理

GlobalExceptionHandler

2.1 说明

全局异常处理器,是 Spring MVC 提供的一种异常处理机制,统一处理由 控制器抛出的异常.

使用全局异常处理器的原因是 Spring MVC 默认的异常处理机制对用户端非常不友好,所以才会使用全局异常处理器手动处理由控制器抛出的异常.

2.2 常用注解
  • @ControllerAdvice

    • 定义全局异常处理器,处理 Controller 中抛出的异常。

  • @RestControllerAdvice

    • 组合注解,是@ControllerAdvice注解和@ResponseBody注解的组合;

    • 用于捕获 Controller 中抛出的异常并对异常进行统一的处理,还可以对返回的数据进行处理。

  • @ExceptionHandler

    • 用于捕获 Controller 处理请求时抛出的异常,并进行统一的处理。

2.3 使用流程
  • 第1步:创建全局异常处理器的类,添加 @RestControllerAdvice 注解;

    • common.exception.GlobalExceptionHandler

  • 第2步:创建对应的异常处理方法,添加 @ExceptionHandler 注解;

    • //注意 注解 和 参数[控制器抛出的异常对象]
      @ExceptionHandler
      public JonsResult doHandleRuntimeException(RuntimeException ex){}
2.4 异常处理顺序

当控制器抛出异常后,异常处理的步骤:

  • 第1步:抛出异常后,首先检查是否定义了全局异常处理器;

  • 第2步:如果未定义,则使用默认的异常处理机制;

  • 第3步:如果已定义,则会在全局异常处理器中定位处理该异常的处理方法;

  • 第4步:如果未找到对应的异常处理方法,则找该异常父类异常的处理方法;

  • 第5步:如果也未找到父类异常的处理方法,则找能处理所有异常的Throwable处理方法;

  • 第6步:如果也未找到 Throwable 方法,则使用默认的异常处理机制[不友好].

2.5 参考代码
package cn.gongxi.weibo.common.exception;
​
import cn.gongxi.weibo.common.response.JsonResult;
import cn.gongxi.weibo.common.response.StatusCode;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
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.RestControllerAdvice;
​
/**
 * 全局异常处理器
 * @ControllerAdvice 注解
 * 将此类标识为全局异常处理器,处理由控制器抛出的异常
 * 如果控制器抛出异常之后,框架会检查是否有全局异常处理器可以处理该异常,
 *       如果有,则调用全局异常处理器进行处理
 *       如果没有,则SpringMVC会抛出异常,由SpringMVC的默认异常处理器进行处理
 *
* @ExceptionHandler 注解
 * 将此注解标识的方法,标识为全局异常处理方法
 *
 * @RestControllerAdvice
 * 组合注解
 * @ControllerAdvice
 * @ResponseBody
 *
 * 控制器异常处理顺序:
 * 1、定位全局异常处理器
 * 2、调用对应的异常处理方法进行处理
 * 3、调用该异常父类异常的处理方法进行处理
 *4、最后由能处理所有异常的异常处理方法进行处理  Throwable
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler
//    @ResponseBody
    public JsonResult doHandleIllegalArgumentException(IllegalArgumentException ex){
        // 在终端输出异常信息
        log.error("IllegalArgumentException" + ex.getMessage());
        return JsonResult.fail(ex.getMessage());
    }
​
    @ExceptionHandler
    public JsonResult doHandleRuntimeException(RuntimeException ex){
        // 在终端输出异常信息
        log.error("RuntimeException" + ex.getMessage());
        return JsonResult.fail(ex.getMessage());
    }
​
    // 当所有的方法都没有处理时,会调用下面方法进行处理
    @ExceptionHandler
    public JsonResult doThrowable(Throwable ex){
        log.error("Throwable" + ex.getMessage());
        return new JsonResult(StatusCode.THROWABLE_ERROR,ex.getMessage());
    }
}
​
;