springboot统一返回值封装

1.统一返回值

1.1前言

如果后端提供的接口没有固定的返回格式,那么前端将会很痛苦。所以在项目中一般都会封装一个返回值类,以确保所有接口都返回固定的格式。这个返回值并没有一个标准的格式,不同公司、不同团队可能封装的不一样,下面推荐一种格式,也是使用比较多的一种格式。

{
	"code": 2003,
	"message": "用户名或密码错误",
	"data": null
}

这种格式主要包含3部分:

  1. code:状态码, 由后端统一定义各种返回结果的状态码
  2. message:描述信息
  3. data:返回的数据,例如列表数据

当然有些人觉得需要加上请求时间 timestamp,还有错误码errorCode ,这些都可以根据实际情况去加。

1.2状态码的定义

我们需要定义一个状态码的枚举类,不同的状态码对应不同的描述,如下

public enum ResponseCode {

    SUCCESS(200,"操作成功!"),
    FAILURE(201,"操作失败"),
    /**系统相关的错误码:5开头**/
    ERROR(500,"系统异常,请稍后重试"),
    /**参数相关的错误码:1开头**/
    PARAM_ERROR(1000,"参数异常"),

    /**权限相关的错误码:2开头**/
    INVALID_TOKEN(2001,"访问令牌不合法"),
    ACCESS_DENIED(2002,"没有权限访问该资源"),
    USERNAME_OR_PASSWORD_ERROR(2003,"用户名或密码错误");

    private final int code;

    private final String message;

    ResponseCode(int code, String message){
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

状态码的定义最好根据不同业务区分一下,例如上面跟参数相关的状态码我们以1开头,跟用户相关的状态码以2开头

1.3返回值类

public class ApiResponse<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    private Integer code;

    private String message;

    private T data;

    public ApiResponse(Integer code,String message){
        this.code = code;
        this.message = message;
    }

    public ApiResponse(Integer code,String message,T data){
        this(code,message);
        this.data = data;
    }

    public static <T> ApiResponse<T> success(){
        return new ApiResponse<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage());
    }

    public static <T> ApiResponse<T> success(T data){
        return new ApiResponse<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(),data);
    }

    public static <T> ApiResponse<T> fail(){
        return new ApiResponse<>(ResponseCode.FAILURE.getCode(),ResponseCode.FAILURE.getMessage());
    }

    public static <T> ApiResponse<T> fail(Integer code,String message){
        return new ApiResponse<>(code,message);
    }

    public static <T> ApiResponse<T> error(){
        return new ApiResponse<>(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMessage());
    }

    public void setCode(Integer code){
        this.code = code;
    }
    public void setMessage(String message){
        this.message = message;
    }
    public void setData(T data){
        this.data = data;
    }
    public Integer getCode(){
        return this.code;
    }
    public String getMessage(){
        return this.message;
    }
    public T getData(){
        return this.data;
    }

    @Override
    public String toString() {
        return (new StringJoiner(", ", ApiResponse.class.getSimpleName() + "[", "]"))
                .add("code=" + this.code)
                .add("message='" + this.message + "'")
                .add("data=" + this.data)
                .toString();
    }
}

这里详细解释一下

1.Serializable接口

ApiResponse继承了Serializable接口, 用于实现序列化功能。当一个类实现了 Serializable 接口后,它的对象可以被转换为字节流并进行存储、传输等操作。

2.泛型T

data是一个泛型,这个也好理解,因为我们返回的数据有可能是一个整型、字符串、集合。

这里为什么不用object?

Object 类型是 Java 中的基类,它可以表示任何类型的对象。Java 中的所有类都是 Object 的子类,因此 Object 类型可以用于引用任何对象。但是,由于 Object 类型是非泛型的,因此在使用它时需要进行类型转换,这可能会导致运行时错误。

总之,泛型 TObject 类型都可以用于表示不确定类型的对象,但是泛型更加类型安全,而 Object 类型则需要进行类型转换。在编写代码时,应该尽可能地使用泛型,以提高代码的可读性和安全性。

3.定义了如下几个方法

//无参,比如说添加用户、编辑用户成功,我们直接返回200,操作成功
success()
    
//比如说请求数据,返回一个列表,我们之直接返回200,操作成功,数据
success(T data)

//无参,比如说添加用户、编辑用户失败,我们直接返回201,操作失败
fail()

//比如说登录失败,我们直接返回自定义的状态码,错误信息
fail(Integer code,String message)

//系统错误,比如你在代码中写了一行1/0(1除以0),直接报500,"系统异常,请稍后重试"
error()

对于error()可能会有这样的疑问:是不是所有的异常都返回500,为啥不异常消息不返回出去?

这也好理解,一般来说系统抛出的异常,我们打印在日志里面即可,不能把异常信息返回给前端。

2.全局异常

上面我们讲到了系统异常,如果系统异常,那它是如何捕抓到的呢?

下面直接上代码

@Slf4j
@RestControllerAdvice
public class GlobalException {

    @ExceptionHandler(Exception.class)
    public ApiResponse<Void> exception(Exception e) {
        log.error("全局异常信息 ex={}", e.getMessage(), e);
        return ApiResponse.error();
    }
}

@RestControllerAdvice

@RestControllerAdvice 是一个 Spring Boot 中用于全局异常处理的注解。它结合了 @ControllerAdvice@ResponseBody 两个注解的功能,可以用于处理所有控制器中抛出的未处理异常,并返回自定义的响应数据。

Exception.class

Exception.class 是 Java 中的一个类对象,它表示了 java.lang.Exception 类。Exception 是所有异常类的父类,它包含了一些通用的方法和属性,例如异常信息、异常堆栈跟踪等。

3.自定义抛出异常

很多时候我们在处理业务逻辑的时候需要抛出一些异常,例如查询某个用户信息,如果用户根本不存在,则需要抛出异常。

public class CustomException extends RuntimeException{
    protected Integer code;
    protected String message;


    public CustomException(Integer code,String message,Throwable e) {
        super(message,e);
        this.code = code;
        this.message = message;
    }

    public CustomException(Integer code,String message){
        this(code,message,null);
    }

    public void setCode(Integer code){
        this.code = code;
    }
    public void setMessage(String message){
        this.message = message;
    }

    public Integer getCode(){
        return this.code;
    }

}

这里的代码比较简单,也不多讲解,可以根据实际情况扩展。

然后把这个异常添加到全局异常类里面去

@ExceptionHandler(value = CustomException.class)
public ApiResponse<Void> customExceptionHandler(CustomException e) {
    return ApiResponse.fail(e.getCode(),e.getMessage());
}

这样我们在任何地方抛出这个异常,都会被捕获到。

例如:

@GetMapping("/test")
public ApiResponse<String> test(){
    throw  new CustomException(ResponseCode.USERNAME_OR_PASSWORD_ERROR.getCode(),ResponseCode.USERNAME_OR_PASSWORD_ERROR.getMessage());
}

得到如下结果:

{
	"code": 2003,
	"message": "用户名或密码错误",
	"data": null
}
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值