代码优雅之道——Springboot统一返回结果

1、为什么要统一返回结果

从前后端分离模式开始后,后端只返回给前端json数据,在实际开发中,为了降低开发人员之间的沟通成本,一般返回结果会定义成一个统一格式,具体的格式根据实际开发业务不同有所区别。从前端的角度思考,调用后端接口后如果成功了则拿到想要的数据或者对应的信息提示,如果不成功也应该有相应的信息提示。从这个角度出发,我们将返回结果设计为:

  • code:响应状态码,根据状态码判断是否成功
  • msg:提示信息,根据调用的接口自定义
  • data:返回的数据

2、返回结果类

import com.example.assertdemo.constant.ResultCodeEnum;
import lombok.Data;

import java.io.Serializable;

/**
* @author LoneWalker
*/
@Data
    public class ApiResult<T> implements Serializable {
        private static final long serialVersionUID = 411731814484355577L;
        /**
* 状态码
*/
        private int code;
        /**
* 提示信息
*/
        private String msg;
        /**
* 相关数据
*/
        private T data;

        public String toString() {
            return "ApiResult(code=" + this.getCode() + ", msg=" + this.getMsg() + ", data=" + this.getData() + ")";
        }

        /**
* 构造器 自定义响应码与提示信息
* @param code    响应码
* @param message 提示信息
*/
        private ApiResult(int code,String message){
            this.code = code;
            this.msg = message;
        }

        /**
* 构造器 自定义响应码、提示信息、数据
* @param code    响应码
* @param message 提示信息
* @param data    返回数据
*/
        private ApiResult(int code,String message,T data){
            this(code,message);
            this.data = data;
        }

        /**
* 成功构造器  无返回数据
*/
        public static <T> ApiResult<T> success(){
            return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage());
        }

        /**
* 成功构造器 自定义提示信息 无返回数据
* @param message 提示信息
*/
        public static <T> ApiResult<T> success(String message){
            return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), message);
        }

        /**
* 成功构造器  有返回数据
*/
        public static <T> ApiResult<T> success(T data){
            return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage(),data);
        }

        /**
* 失败构造器  无返回数据
*/
        public static <T> ApiResult<T> fail(){
            return new ApiResult<>(ResultCodeEnum.FAIL.getCode(), ResultCodeEnum.FAIL.getMessage());
        }

        /**
* 失败构造器 自定义提示信息 无返回数据
* @param message 提示信息
*/
        public static <T> ApiResult<T> fail(String message){
            return new ApiResult<>(ResultCodeEnum.FAIL.getCode(), message);
        }

        /**
* 失败构造器  有返回数据
*/
        public static <T> ApiResult<T> fail(T data){
            return new ApiResult<>(ResultCodeEnum.FAIL.getCode(), ResultCodeEnum.FAIL.getMessage(),data);
        }
    }

3、状态码及提示信息枚举

除去基础的操作成功或失败,可以将状态码归类,例如请求参数问题归纳到1001-1999之间。

import lombok.Getter;

/**
 * @author LoneWalker
 */

@Getter
public enum ResultCodeEnum{

    /**
     * success
     */
    SUCCESS(0,"操作成功"),

    /**
     * fail
     */
    FAIL(-1,"操作失败"),

    /**
     * 参数错误:1001-1999
     */
    PARAM_IS_INVALID(1001,"参数无效"),
    PARAM_TYPE_ERROR(1002,"参数类型错误"),
    ;

    /**
     * 状态码
     */
    private final int code;

    /**
     * 提示信息
     */
    private final String message;

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

4、使用与测试

controller

import com.example.assertdemo.common.ApiResult;
import com.example.assertdemo.entity.Contract;
import com.example.assertdemo.req.AddContractReq;
import com.example.assertdemo.req.QueryContractReq;
import com.example.assertdemo.service.ContractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/contract/")
public class ContractController {

    @Autowired
    private ContractService contractService;

    @PostMapping("add")
    public ApiResult addContract(@RequestBody AddContractReq request) {
        return contractService.addContract(request);
    }

    @PostMapping("list")
    public ApiResult<List<Contract>> listContract(@RequestBody QueryContractReq request) {
        return contractService.listContract(request);
    }
}

新增或查询参数类

import lombok.Data;

/**
 * @author LoneWalker
 * @date 2022/8/28
 * @description
 */
@Data
public class AddContractReq {

    /**
     * 合同编号
     */
    private String code;
    /**
     * 合同名称
     */
    private String name;
    /**
     * 客户名称
     */
    private String customer;
}
import lombok.Data;

/**
 * @author LoneWalker
 * @date 2022/8/28
 * @description
 */
@Data
public class QueryContractReq {

    /**
     * 合同名称
     */
    private String contractName;

    /**
     * 客户名称
     */
    private String customer;
}

Service

import com.example.assertdemo.common.ApiResult;
import com.example.assertdemo.entity.Contract;
import com.example.assertdemo.req.AddContractReq;
import com.example.assertdemo.req.QueryContractReq;

import java.util.List;

public interface ContractService {

    /**
     * 添加合同信息
     * @param request
     * @return
     */
    ApiResult addContract(AddContractReq request);

    /**
     * 合同列表数据
     * @param request 查询参数
     * @return
     */
    ApiResult<List<Contract>> listContract(QueryContractReq request);
}

impl

import com.example.assertdemo.common.ApiResult;
import com.example.assertdemo.entity.Contract;
import com.example.assertdemo.req.AddContractReq;
import com.example.assertdemo.req.QueryContractReq;
import com.example.assertdemo.service.ContractService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service("contractService")
public class ContractServiceImpl implements ContractService {
    @Override
    public ApiResult addContract(AddContractReq request) {
        //存入数据库
        return ApiResult.success();
    }

    @Override
    public ApiResult<List<Contract>> listContract(QueryContractReq request) {
        //模拟数据
        List<Contract> list = new ArrayList<>();
        Contract contractOne = new Contract(1L, "HT082801", "临床试验一期合同", "省立医院");
        Contract contractTwo = new Contract(2L, "HT082802", "临床试验二期合同", "省立医院");
        Contract contractThree = new Contract(3L, "HT082803", "临床试验三期合同", "省立医院");
        list.add(contractOne);
        list.add(contractTwo);
        list.add(contractThree);
        return ApiResult.success(list);
    }
}

启动项目,新增一条数据

查询数据

5、问题

以上都是接口调用成功的情况,当业务出现异常,按照我们的初衷,需要将业务异常结果组装成统一的信息返回给前端进行提示。

下一篇 代码优雅之道——断言 + Springboot统一异常处理


版权声明:本文为QingXu1234原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。