JAVA项目封装返回数据及统一异常处理

一.封装数据

1.定义一个通用返回类

        名字由自己取,我这取名为BaseResponse。作用是封装返回数据,让所有的返回数据都按该类的格式返回数据。通用返回类的代码如下:

package com.example.usermanage.common;

import lombok.Data;

import java.io.Serializable;

/**
 * 通用返回类
 *
 * @author zeng
 * @param <T>
 */
@Data
public class BaseResponse<T> implements Serializable {

    private int code;

    private T data;

    private String message;

    private String description;


    /**
     * 构造函数
     * @param code 状态码
     * @param data 向前端传的数据
     * @param message 操作反馈信息:如登录成功、查询成功、查询失败、账号密码错误等提示信息。有利于后续纠错。
     * @param description 反馈信息的详细描述
     */
    public BaseResponse(int code, T data, String message,String description) {
        this.code = code;
        this.data = data;
        this.message = message;
        this.description=description;
    }

    /**
     *定义各种构造函数,有利于灵活的返回各种信息。如下面这个只返回状态码、数据、操作反馈信息,但不返回详细描述。
     * 而再下一个只返回code和data,不返回message和description的信息。
     */
    public BaseResponse(int code, T data,String message) {
        this(code,data,message,"");
    }

    public BaseResponse(int code, T data) {
        this(code,data,"","");
    }

    public BaseResponse(ErrorCode errorCode){
        this(errorCode.getCode(),null,errorCode.getMessage(),errorCode.getDescription());
    }
    public BaseResponse(ErrorCode errorCode,String description){
        this(errorCode.getCode(),null,errorCode.getMessage(),description);
    }
}

2.通用返回的工具类

        名字由自己取,我这取名为ResultUtils。它的功能是帮我们创建BaseResponse对象。里面也是重载了各种构造函数供我们灵活使用。

package com.example.usermanage.common;

/**
 * 返回类型工具类
 *
 * @author zeng
 */
public class ResultUtils {

    /**
     * 成功
     * @param data
     * @param <T>
     * @return
     */
    public static <T> BaseResponse<T> success(T data) {
        return new BaseResponse<>(0, data, "ok");
    }

    /**
     * 成功
     * @param data
     * @return
     */
    public static BaseResponse success(int data) {
        return new BaseResponse(0, data, "ok");
    }

    /**
     * 失败
     * @param errorCode
     * @return
     */
    public static BaseResponse error(ErrorCode errorCode){
        return new BaseResponse<>(errorCode.getCode(),null,errorCode.getMessage(),errorCode.getDescription())   ;
    }

    /**
     * 失败
     * @param errorCode
     * @return
     */
    public static BaseResponse error(ErrorCode errorCode,String message,String description){
        return new BaseResponse<>(errorCode.getCode(),description)   ;
    }

    /**
     * 失败
     * @param code
     * @return
     */
    public static BaseResponse error(int code,String message,String description){
        return new BaseResponse<>(code,null,message,description)   ;
    }
    /**
     * 失败
     * @param errorCode
     * @return
     */
    public static BaseResponse error(ErrorCode errorCode,String description){
        return new BaseResponse<>(errorCode.getCode(),null,errorCode.getMessage(),description)   ;
    }

}

3.使用ResultUtils工具类与不使用ResultUtils工具类的区别,以如下接口实例为例:

         未使用ResultUtils工具类:

    /**
     * 用户注册接口
     *
     * @param userRequest 用户注册时需传递的参数
     * @return 返回用户的id信息
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRequest userRequest) {
        if (userRequest == null) {
            return;
        }
        String userAccount = userRequest.getUserAccount();
        String userPassword = userRequest.getUserPassword();
        String checkPassword = userRequest.getCheckPassword();
        String planetCode = userRequest.getPlanetCode();
         /**
          *这里用到的是一个工具,叫commons。
          *StringUtils.isAnyBlank(userAccount, userPassword, checkPassword,planetCode)这段
          *代码的作用是判断字段是否为空或是否为空字符串。
          *直接再maven中引入即可,maven坐标如下
          * <dependency>
          * <groupId>org.apache.commons</groupId>
          * <artifactId>commons-lang3</artifactId>
          * <version>3.12.0</version>
          * </dependency>
          */
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword,planetCode)) {
            return;
        }
        long result = userService.userRegister(userAccount, userPassword, checkPassword, planetCode);
        return new BaseResponse<>(0,result,"ok");

    }

        使用ResultUtils工具类:

    /**
     * 用户注册接口
     *
     * @param userRequest 用户注册时需传递的参数
     * @return 返回用户的id信息
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRequest userRequest) {
        if (userRequest == null) {
            return;
        }
        String userAccount = userRequest.getUserAccount();
        String userPassword = userRequest.getUserPassword();
        String checkPassword = userRequest.getCheckPassword();
        String planetCode = userRequest.getPlanetCode();
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword,planetCode)) {
            return;
        }
        long result = userService.userRegister(userAccount, userPassword, checkPassword, planetCode);
        return ResultUtils.success(result);

    }

        两者之间的区别就在方法中的最后一段代码。 未使用ResultUtils工具类时,每次都需要自己手动创建对象进行数据封装。而使用工具类时,直接通过类名调用类中的对应的构造方法进行对象的创建及数据的封装。虽然看似都是一行代码,使用工具类比不使用还要多写工具类将代码的编写复杂化了,但是这便是java中重要的封装思想,且有了工具类后统一异常处理中会更加方便。

二.统一异常处理

1.定义各种返回码

        如浏览器中常见的404、500、200等。注意该类为枚举类型(enum)而不是普通类。代码如下:

package com.example.usermanage.common;

/**
 * 返回码
 *
 * @author zeng
 */
public enum ErrorCode {

    SUCCESS(0,"ok","") ,
    PARAMS_ERROR(40000,"请求参数错误",""),
    NULL_ERROR(40001,"请求参数为空",""),
    NO_LOGIN(40100,"未登录",""),
    NO_AUTH(40101,"暂无权限访问",""),
    SYSTEM_ERROR(50000,"系统内部异常","")
    ;
    //返回码
    private final int code;
    //操作响应信息
    private final String message;
    //响应信息的详细描述
    private final String description;

    //构造函数
    ErrorCode(int code, String message, String description) {
        this.code = code;
        this.message = message;
        this.description = description;
    }
    //get方法
    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public String getDescription() {
        return description;
    }
}

        定义一套自己的规范的响应返回码及其描述信息,有利与团队协作。如前端开发在获取到后端的返回数据后,需要给用户一些操作响应后的提示信息。如用户在查询数据时,未查询到任何数据,用户向我们反馈系统bug,我们通过测试进行模拟查询后,便能通过我们的状态码来知道是什么问题,如系统给我们返回的是40101(NO_AUTH),其对应的是无访问权限,那我们就知道他查询的数据为空是因为他没有访问权限并不是系统bug。状态码定义的越详细,我们在后期维护中就越能快速精准的定位到错误的原因。

2.定义业务异常类

        相对与java的异常类,自定义的异常类可支持更多的字段,该类中的code和description便是我们自己添加的。让其在进行异常处理是给我们返回状态码和对应的描述信息。

package com.example.usermanage.exception;

import com.example.usermanage.common.ErrorCode;

/**
 * 业务异常类
 * 继承RuntimeException异常处理类。
 */
public class BusinessException extends RuntimeException{
    private  int code;
    private  String description;

    /**
     * 各种构造函数,供我们灵活的使用
     */
    public BusinessException(String message, int code, String description) {
        super(message);
        this.code = code;
        this.description = description;
    }

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
        this.description = errorCode.getDescription();
    }

    public BusinessException(ErrorCode errorCode,String description) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}

3.编写全局异常处理器

        捕获代码中所有的异常,内部消化,让前端得到更详细的业务报错 / 信息,同时屏蔽掉项目框架本身的异常(不暴露服务器内部状态),对各异常信息集中处理,比如记录日志。

package com.example.usermanage.exception;

import com.example.usermanage.common.BaseResponse;
import com.example.usermanage.common.ErrorCode;
import com.example.usermanage.common.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 * Slf4j是用来记录日志信息的,lombok中自带的。
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public BaseResponse businessExceptionHandler(BusinessException e){
        log.error("BusinessException"+e.getMessage(),e);
        //return new BaseResponse(e.getCode(),e.getMessage(),e.getDescription());
        return ResultUtils.error(e.getCode(),e.getMessage(),e.getDescription());
    }

    @ExceptionHandler(RuntimeException.class)
    public BaseResponse runtimeExceptionHandler(RuntimeException e){
        //集中处理
        log.error("RuntimeException",e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR,e.getMessage());
    }
}

4.区别

        不使用异常处理和使用异常处理的区别,也是以一个接口实例为例,如下:

        未使用自定义异常处理:

 /**
     * 用户注册接口
     *
     * @param userRequest 用户注册时需传递的参数
     * @return 返回用户的id信息
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRequest userRequest) {
        if (userRequest == null) {
            //throw new BusinessException(ErrorCode.NULL_ERROR,"用户请求为空");
            return;
        }
        String userAccount = userRequest.getUserAccount();
        String userPassword = userRequest.getUserPassword();
        String checkPassword = userRequest.getCheckPassword();
        String planetCode = userRequest.getPlanetCode();
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword,planetCode)) {
            //throw new BusinessException(ErrorCode.PARAMS_ERROR,"注册输入信息不能为空");
            return;
        }
        long result = userService.userRegister(userAccount, userPassword, checkPassword, planetCode);
        return ResultUtils.success(result);

    }

        使用自定义异常处理:

 /**
     * 用户注册接口
     *
     * @param userRequest 用户注册时需传递的参数
     * @return 返回用户的id信息
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRequest userRequest) {
        if (userRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR,"用户请求为空");
            //return;
        }
        String userAccount = userRequest.getUserAccount();
        String userPassword = userRequest.getUserPassword();
        String checkPassword = userRequest.getCheckPassword();
        String planetCode = userRequest.getPlanetCode();
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword,planetCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"注册输入信息不能为空");
            //return;
        }
        long result = userService.userRegister(userAccount, userPassword, checkPassword, planetCode);
        return ResultUtils.success(result);

    }

        很明显,使用了自定义异常处理后,在抛出异常时可以抛出自己定义好的返回码及错误提示信息。让异常的可读性极大的提升,也便于前端阅读及向用户反馈其操作的异常信息。

ps:此文章的内容,是在学习 yupi的项目时自己做的一些笔记信息。

Spring Boot 中,我们可以使用统一返回处理来规范 API 接口返回格式,避免重复的代码和降低维护成本。 一般来说,我们可以定义一个 Result 类来封装接口返回数据,包括状态码、提示信息和返回数据等。同时,我们还可以定义一个异常处理类来捕获全局的异常,并将异常信息封装到 Result 类中返回给前端。 以下是一个示例代码: ```java @Data @NoArgsConstructor @AllArgsConstructor public class Result<T> { private Integer code; private String msg; private T data; public static <T> Result<T> success(T data) { return new Result<>(200, "success", data); } public static Result<?> error(Integer code, String msg) { return new Result<>(code, msg, null); } } @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) public Result<?> handleException(Exception e) { log.error("未知异常:{}", e.getMessage()); return Result.error(500, "服务器内部错误"); } @ExceptionHandler(value = BusinessException.class) public Result<?> handleBusinessException(BusinessException e) { log.error("业务异常:{}", e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } } ``` 在上面的代码中,我们定义了一个 Result 类来封装接口返回数据。其中,success 方法表示成功时返回的结果,error 方法表示失败时返回的结果。 同时,我们还定义了一个全局异常处理器 GlobalExceptionHandler,用来捕获全局的异常。在 handleException 和 handleBusinessException 方法中,我们分别处理了未知异常和业务异常,并将异常信息封装到 Result 类中返回给前端。 通过使用统一返回处理,我们可以大大简化接口返回的代码,提高代码的可维护性和可读性。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值