如何优雅地写后端API接口

在移动互联网,分布式、微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式。前后端的工作职责越来越明确,现在的前端都称之为大前端,技术栈以及生态圈都已经非常成熟;以前后端人员瞧不起前端人员,那现在后端人员要重新认识一下前端,前端已经很成体系了

1.接口交互

前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。

针对URL路径的restful风格,以及传入参数的公共请求头的要求(如:app_version,api_version,device等),这里就不介绍了,小伙伴们可以自行去了解,也比较简单。

着重介绍一下后端服务器如何实现把数据返回给前端?

2.返回格式

后端返回给前端我们采用json格式

{
  "success": boolean,
  "code": int,
  "data": object,
  "msg": string
}
  • success是否成功

最简单的判断,前端直接取该值判断是否操作成功,成功为true,失败为false

  • code状态码

当一个操作会因为多种因素失败的情况下,每种失败的原因进行设置不同的状态码。可以按照业务区分,例如用户的错误状态码在101-199之间,系统的错误状态码在201-299之间,可以按照自己的业务进行设置,我们参考下http状态码:

200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
  • data数据

这里是具体要返回的数据,数据格式可以为数组、对象、字符串等格式

  • msg消息

这个字段相对理解比较简单,就是发生错误时,如何友好的进行提示。一般的设计是和code状态码一起设计。如登陆失败> code:203,msg:登陆失败,账号或者密码有误

2.1.返回样例

这就是一个操作成功返回的例子

{
  "success": true,
  "code": 1,
  "data": {"k": "v"},
  "msg": "操作成功"
}


3.实现

3.1.定义状态码枚举类

package com.niu.demo.util;

/**
 * @description: 状态吗设置
 * @author: nxq email: niuxiangqian163@163.com
 * @createDate: 2020/12/21 7:46 下午
 * @updateUser: nxq email: niuxiangqian163@163.com
 * @updateDate: 2020/12/21 7:46 下午
 * @updateRemark:
 * @version: 1.0
 **/
public enum ResultCode {
	/* 通用状态码 成功与失败 */
	SUCCESS(1,"ok"), FAILED(-1,"操作失败"),
	/* 参数错误  101 - 199*/
	PARAM_IS_INVALID(101,"参数无效"),
	PARAM_IS_BLANK(101,"参数为空"),
	/* 用户错误  201 - 299  */
	USER_NOT_LOGIN(201,"未登录"),
	USER_NOT_EXIST(202,"用户不存在"),
	USER_LOGIN_ERROR(203,"登陆失败,账号或者密码有误"),
	NOT_PERMISSION(204,"无权限访问"),
	/* 业务错误 301 - 399*/
	DATA_NOT_FOUND(301,"没有数据"),
	//.......更多
	;
	private Integer code;
	private String msg;


	ResultCode(Integer code, String msg) {
		this.code=code;
		this.msg=msg;
	}

	public Integer getCode() {
		return code;
	}

	public String getMsg(){
		return msg;
	}
}


3.2.创建返回结果类

package com.niu.demo.util.core;

import com.niu.demo.util.ResultCode;
import java.io.Serializable;

/**
 * @description: 响应类
 * @author: nxq email: niuxiangqian163@163.com
 * @createDate: 2020/12/21 7:37 下午
 * @updateUser: nxq email: niuxiangqian163@163.com
 * @updateDate: 2020/12/21 7:37 下午
 * @updateRemark:
 * @version: 1.0
 **/
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;
    private int code;  //状态码
    private boolean success; //是否成功
    private String msg; //提示信息
    private T data; //数据

    public static <T> R<T> ok() {
        return restResult(null, ResultCode.SUCCESS.getCode(),  ResultCode.SUCCESS.getMsg());
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg());
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, ResultCode.SUCCESS.getCode(), msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg());
    }
    public static <T> R<T> failed(String msg) {
        return restResult(null, ResultCode.FAILED.getCode(), msg);
    }

    public static <T> R<T> failed(ResultCode resultCode) {
        return restResult(null, resultCode.getCode(), resultCode.getMsg());
    }
    public static <T> R<T> failed(Integer code,String msg) {
        return restResult(null, code, msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, ResultCode.FAILED.getCode(), ResultCode.SUCCESS.getMsg());
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, ResultCode.FAILED.getCode(), msg);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setSuccess(code == 1);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }


}

3.3.在控制器中使用

@RequestMapping
@RestController
public class DemoController {

    @GetMapping("/test1")
    public R<Object> test1(){
        return R.ok();
    }

    @GetMapping("/test2")
    public R<String> test2(){
        return R.ok("uuid");
    }

    @GetMapping("/test3")
    public R<List<String>> test3(){
        List<String> list = new ArrayList<>();
        return R.ok(list);
    }

    @GetMapping("/test4")
    public R<Object> test4(){
        return R.failed();
    }

    @GetMapping("/test5")
    public R<Object> test5(){
        return R.failed("暂时不能进行修改");
    }

    @GetMapping("/test6")
    public R<Object> test6(){
        return R.failed(ResultCode.USER_LOGIN_ERROR);
    }
    
    @GetMapping("/test7")
    public R<Object> test7(){
        return R.failed(500,"内部错误,请联系管理员");
    }
}

4.补充

这个方案还有没有别的优化空间,当然是有的。例如:每个接口都要返回下R这个类,其实最主要的数据还是R的data数据对象,可以优化一下只返回数据对象,在拦截器里再包装一下数据对象。

如果大家有更好的方式可以在评论区交流一下

正文到此结束
评论插件初始化中...
Loading...