如何优雅地写后端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数据对象,可以优化一下只返回数据对象,在拦截器里再包装一下数据对象。
如果大家有更好的方式可以在评论区交流一下
正文到此结束
相关文章
热门推荐
评论插件初始化中...