API接口的RESTful设计
前言:今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常。
一般系统的大致整体架构图如下:
因为本篇博客主要介绍的是API接口,其他的模块比如网关、Redis缓存、MQ消息中间件小伙伴们自行去补充。
一、前后端RESTful接口交互
前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。
1.1、RESTful风格的URL路径设计
REST,即Representational State Transfer的缩写。翻译过来是表现层状态转换。实际上 REST 的全称是 Resource Representational State Transfer ,直白地翻译过来就是 “资源”在网络传输中以某种“表现形式”进行“状态转移” 。如果一个架构符合REST原则,就称它为RESTful架构。
REST由Roy Fielding在他的论文中提出。REST用来描述客户端通过某种形式获取服务器的数据,这些数据资源的格式通常是JSON或XML。同时,这些资源的表现或资源的集合是可以修改的,伴随着行为和关系可以通过多媒体来发现。在我看来,一种简单的理解就是:在设计API时,使用路径定位资源,方法定义操作,通过Content-Type和Accept来协商资源的类型。
资源(Resource) :我们可以把真实的对象数据称为资源。一个资源既可以是一个集合,也可以是单个个体。比如我们的班级 classes 是代表一个集合形式的资源,而特定的 class 代表单个个体资源。每一种资源都有特定的 URI(统一资源标识符)与之对应,如果我们需要获取这个资源,访问这个 URI 就可以了,比如获取特定的班级:
/class/12
。另外,资源也可以包含子资源,比如/classes/classId/teachers
:列出某个指定班级的所有老师的信息表现形式(Representational):"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式比如
json
,xml
,image
,txt
等等叫做它的"表现层/表现形式"。状态转移(State Transfer) :大家第一眼看到这个词语一定会很懵逼?内心 BB:这尼玛是啥啊? 大白话来说 REST 中的状态转移更多地描述的服务器端资源的状态,比如你通过增删改查(通过 HTTP 动词实现)引起资源状态的改变。ps:互联网通信协议 HTTP 协议,是一个无状态协议,所有的资源状态都保存在服务器端。
1.2、RESTful规范
方法动作
GET:查询操作,举个例子:
GET /classes
(获取所有班级)POST:修改/添加操作,举个例子:
POST /classes
(创建班级)PUT:更新操作,举个例子:
PUT /classes/12
(更新编号为 12 的班级)DELETE:删除操作,举个例子:
DELETE /classes/12
(删除编号为 12 的班级)PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。
接口路径命名规范
表示 API 的具体网址。实际开发中常见的规范如下:
接口网址尽量使用名词,避免使用动词,API 中的名词也应该使用复数。 因为 RESTful API 操作(HTTP Method)的是资源(名词)而不是动作(动词),REST 中的资源往往和数据库中的表对应,而数据库中的表都是同种记录的"集合"(collection)。如果 API 调用并不涉及资源(如计算,翻译等操作)的话,可以用动词。比如:
GET /calculate?param1=11¶m2=33
。不用大写字母,建议用中杠 - 不用下杠 _ 。比如邀请码写成
invitation-code
而不是 invitation_code 。善用版本化 API。当我们的 API 发生了重大改变而不兼容前期版本的时候,我们可以通过 URL 来实现版本化,比如
http://api.example.com/v1
、http://apiv1.example.com
。版本不必非要是数字,只是数字用的最多,日期、季节都可以作为版本标识符,项目团队达成共识就可。
RESTful为什么加版本号
所有的API必须保持向后兼容,必须在引入新版本API的同时确保旧版本API仍然可用。所以应该为其提供版本支持。必须在URL中嵌入版本编号,格式要求如下:http://URL/api/v1/*
一般来说,API接口是提供给其他系统或是其他公司使用,不能随意频繁的变更。然而,需求和业务不断变化,接口和参数也会发生相应的变化。如果直接对原来的接口进行修改,势必会影响线其他系统的正常运行。这就必须对api 接口进行有效的版本控制。
例如,添加用户的接口,由于业务需求变化,接口的字段属性也发生了变化而且可能和之前的功能不兼容。为了保证原有的接口调用方不受影响,只能重新定义一个新的接口。
http://localhost:8080/api/v1/user
http://localhost:8080/api/v2/user
Api 版本控制的方式:
请求url 路径区分,在同一个域名下使用不同的url路径,test.com/api/v1/,test.com/api/v2
域名区分管理,即不同的版本使用不同的域名,v1.api.test.com,v2.api.test.com
请求参数区分,在同一url路径下,增加version=v1或v2 等,然后根据不同的版本,选择执行不同的方法。
实际项目中,一般选择第一种:请求url路径区分。因为第一种既能保证水平扩展,又不影响以前的老版本
1.3、RESTful URL设计
RESTful风格的url能实现自我功能的描述,清晰明了。
user/{uid}/friends //用户/用户id/用户的朋友,表示用户的好友列表,语义清晰明了,符合RESTful风格
user/friends/uid //不符合RESTful风格,url语义不顺畅
item/{id}/delete //这里delete可以视为名词,商品/商品id/删除,语义清晰明了,符合RESTful风格
item/delete/{id} //商品/删除/商品id,url语义不明了,不清晰
针对RESTful风格URL路径的传入参数的公共请求头的要求(如:app_version,api_version,device等),这里就不介绍了。
1.4、RestFul接口举例
Talk is cheap!来举个实际的例子来说明一下吧!现在有这样一个 API 提供班级(class)的信息,还包括班级中的学生和教师的信息,则它的路径应该设计成下面这样。
GET /classes:列出所有班级
POST /classes:新建一个班级
GET /classes/{classId}:获取某个指定班级的信息
PUT /classes/{classId}:更新某个指定班级的信息(一般倾向整体更新)
PATCH /classes/{classId}:更新某个指定班级的信息(一般倾向部分更新)
DELETE /classes/{classId}:删除某个班级
GET /classes/{classId}/teachers:列出某个指定班级的所有老师的信息
GET /classes/{classId}/students:列出某个指定班级的所有学生的信息
DELETE /classes/{classId}/teachers/{ID}:删除某个指定班级下的指定的老师的信息
反例:
/getAllclasses
/createNewclass
/deleteAllActiveclasses
理清资源的层次结构,比如业务针对的范围是学校,那么学校会是一级资源:/schools
,老师: /schools/teachers
,学生: /schools/students
就是二级资源。
1.5、@GetMapping、@PostMapping、@PutMapping和@DeleteMapping
从Spring4.3开始引进了{@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping},提供了对Restful风格的支持,来帮助简化常用的HTTP方法的映射,并更好地表达被注解方法的语义。
@GetMapping,处理get请求
@PostMapping,处理post请求
@PutMapping,处理put请求
@DeleteMapping,处理delete请求
以@GetMapping和@PostMapping为例,Spring官方文档说:
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。
@PostMapping 是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。该注解将HTTP Post 映射到 特定的处理方法上。
二、统一的API接口返回格式
着重介绍一下后端服务器如何实现把数据返回给前端,后端返回给前端我们一般用JSON体方式,定义如下:
{#返回状态码code:integer, #返回信息描述message:string,#返回值data:object
}
2.1、CODE状态码
code返回状态码,一般小伙伴们是在开发的时候需要什么,就添加什么。
如接口要返回用户权限异常,我们加一个状态码为101吧,下一次又要加一个数据参数异常,就加一个102的状态码。这样虽然能够照常满足业务,但状态码太凌乱了
我们应该可以参考HTTP请求返回的状态码
下面是常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
我们可以参考这样的设计,这样的好处就把错误类型归类到某个区间内,如果区间不够,可以设计成4位数。
#1000~1999 区间表示参数错误
#2000~2999 区间表示用户错误
#3000~3999 区间表示接口异常
这样前端开发人员在得到返回值后,根据状态码就可以知道,大概什么错误,再根据message相关的信息描述,可以快速定位。
2.2、Message
这个字段相对理解比较简单,就是发生错误时,如何友好的进行提示。一般的设计是和code状态码一起设计,如:
再在枚举中定义,状态码:
状态码和信息就会一一对应,比较好维护。
2.3、Data
返回数据体,JSON格式,根据不同的业务有不同的JSON体。
2.4、API接口统一返回使用举例
(1) 我们要设计一个返回体类Result
(2)控制层Controller
我们会在controller层处理业务请求,并返回给前端,以order订单为例
我们看到在获得order对象之后,我们是用的Result构造方法进行包装赋值,然后进行返回。小伙伴们有没有发现,构造方法这样的包装是不是很麻烦,我们可以优化一下。
(3)美观美化
我们可以在Result类中,加入静态方法,一看就懂
那我们来改造一下Controller:
上面我们看到在Result类中增加了静态方法,使得业务处理代码更加简洁。
三、设计一个返回体类ResultBean类
package com.hs.demo.pojo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;/*** 统一返回 ResultBean:将所有的接口的响应数据的格式进行统一。** @Data 注解的主要作用是提高代码的简洁,使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法;要使用 @Data 注解要先引入lombok,lombok 是什么,它是一个工具类库,可以用简单的注解形式来简化代码,提高开发效率。*/@Data
@ApiModel("固定返回格式")
public class ResultBean implement Serializable{private static final long serialVersionUID = 1L;/*** 错误码*/@ApiModelProperty("错误码")private Integer code;/*** 提示信息*/@ApiModelProperty("提示信息")private String message;/*** 具体的内容*/@ApiModelProperty("响应数据")private Object data;}
四、API全局返回码
描述API接口的共性返回码,API自定义的接口返回码请参阅对应API接口文档描述。
API全局返回码,就是每次调用接口时,可能获得正确或错误的返回码,开发者可以根据返回码信息调试接口,排查错误。
百度Open API错误码定义:
错误码 |
错误描述 |
Error Description |
0 |
成功 |
Success |
1 |
未知错误 |
Unknown error |
2 |
服务暂不可用 |
Service temporarily unavailable |
3 |
未知的方法 |
Unsupported openapi method |
4 |
接口调用次数已达到设定的上限 |
Open api request limit reached |
5 |
请求来自未经授权的IP地址 |
Unauthorized client IP address |
6 |
无权限访问该用户数据 |
No permission to access user data |
7 |
来自该refer的请求无访问权限 |
No permission to access data for this referer |
100 |
请求参数无效 |
Invalid parameter |
101 |
api key无效 |
Invalid API key |
104 |
无效签名 |
Incorrect signature |
105 |
请求参数过多 |
Too many parameters |
106 |
未知的签名方法 |
Unsupported signature method |
107 |
timestamp参数无效 |
Invalid/Used timestamp parameter |
109 |
无效的用户资料字段名 |
Invalid user info field |
110 |
无效的access token |
Access token invalid or no longer valid |
111 |
access token过期 |
Access token expired |
210 |
用户不可见 |
User not visible |
211 |
获取未授权的字段 |
Unsupported permission |
212 |
没有权限获取用户的email |
No permission to access user email |
800 |
未知的存储操作错误 |
Unknown data store API error |
801 |
无效的操作方法 |
Invalid operation |
802 |
数据存储空间已超过设定的上限 |
Data store allowable quota was exceeded |
803 |
指定的对象不存在 |
Specified object cannot be found |
804 |
指定的对象已存在 |
Specified object already exists |
805 |
数据库操作出错,请重试 |
A database error occurred. Please try again |
900 |
访问的应用不存在 |
No such application exists |
五、授权/令牌请求接口返回码
描述应用发起授权请求或令牌请求时,开放平台的返回码。
错误码 |
错误描述 |
Error Description |
10000 |
非法的请求参数 |
Invalid request |
10001 |
用户认证失败 |
Invalid client |
10002 |
非法的授权信息 |
Invalid grant |
10003 |
应用没有被授权,无法使用所指定的grant_type |
Unauthorized client |
10004 |
grant_type字段超过定义范围 |
Unsupported grant_type |
10005 |
scope信息无效或超出范围 |
Invalid scope |
10006 |
提供的更新令牌已过期 |
Expired token |
10007 |
redirect_uri字段与注册应用时所填写的不匹配 |
Redirect_uri mismatch |
10008 |
response_type参数值超过定义范围 |
Unsupported response type |
10009 |
用户或授权服务器拒绝授予数据访问权限 |
Access denied |
参考文档:
SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!
RestFul API 简明教程
restful接口实战 更新用put 新增用post 获取get 删除detele
公司前端和后端因为接口吵起来啦,还列了 5 锅罪
API接口的RESTful设计相关推荐
- 对外API接口的安全性设计及鉴权方式
对外API接口的安全性设计及鉴权方式 API鉴权方式 API Key + API Secret实现API鉴权 Cookie + Session实现API鉴权 token机制实现API鉴权 API接口的 ...
- API接口应该如何设计?
在日常开发中,总会接触到各种接口.前后端数据传输接口,第三方业务平台接口.一个平台的前后端数据传输接口一般都会在内网环境下通信,而且会使用安全框架,所以安全性可以得到很好的保护.这篇文章重点讨论一下提 ...
- 四连问:API 接口应该如何设计?如何保证安全?如何签名?如何防重?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:cnblogs.com/jurendage/p/126538 ...
- API 接口应该如何设计?如何保证安全?如何签名?如何防重?
点击蓝色"架构文摘"关注我哟 加个"星标",每天上午 09:25,干货推送! 来源:https://www.cnblogs.com/jurendage/p/12 ...
- c# api接口管理
在一个大型项目中,API接口的数量会不断增加,这会使API管理过程变得非常困难.因此,如何进行有效的管理就变得至关重要.下面是一些可以用来管理API接口的方法可以用来管理API接口的建议: 一.组织接 ...
- 64如何传入后台_如何保证API接口数据安全?
(给ImportNew加星标,提高Java技能) 转自:Java互联网架构师小马 链接:https://www.jianshu.com/p/e2d362ede89f 前后端分离的开发方式,我们以接口为 ...
- 安全架构-api接口安全设计
安全架构-api接口安全设计 安全架构系列文章是从api接口的安全性设计引入的,讨论了api的业务安全-幂等性设计,传输安全,加签名和加解密,介绍了对称加密,非对称加密的常用算法的实现.继续回到api ...
- restful api接口设计
技术由来: 互联网早期,页面请求和并发量不高,且移动端未盛行时对接口要求不高,使用动态页面(jsp)就能满足绝大多数的使用需求.但是随着互联网和移动设备的发展,人们对Web应用的使用需求也增加,传统的 ...
- restful 参数带斜杠_SpringCloud实战:Restful架构API接口经典设计误区
前言 目前微服务架构盛行,发现很多同学设计业务API接口时,写法五花八门.现总结下目前项目上,设计业务API接口的经典误区写法. API接口设计经典误区写法 1.查询某个对象接口: GET /app/ ...
最新文章
- MS UC 2013-0-虚拟机-标准化-部署-2-模板机-制作-2-设置-虚拟机
- EditPlus 3.5 版已经发布
- error while loading shared libraries:libmysqlclient.so.18 错误
- gitlab 自动推送代码到gitee_Gitlab 利用 Webhook+jenkins 实现自动构建与部署
- select框怎么传值到服务端_前端简历中的项目经历怎么突出亮点?
- linux之通过tail命令动态跟踪日志文件里面的末尾信息
- 团队项目讨论及计划修订版
- 95-32-010-ChannelPipeline-ChannelPipeline简介
- java 获取js html_JS获取网页中HTML元素的几种方法
- 2018结束,新的开始 - CMU学弱走上技术之路
- 国内外智慧医疗云平台调研
- 九月英语--不同以往的感觉
- python中localtime和gtime的区别及时区计算的代码
- ct上的img表示什么_X线/CT/MR影像片子上的标识你是否都认识?
- .Net Core MVC引入static静态变量到.cshtml页面
- 纹理打包器 TexturePacker
- 笔记本F1音量键常亮,电脑没声音?这里有妙招。
- 柴达木光伏+农业跨界融合新态势
- uva 672 Gangsters( dp )
- uM计算机组成原理,计算机组成原理_课程设计报告.doc
热门文章
- mysql 查询表 第一列报错_MySQL----DQL(查询数据库表中数据)
- OpenCV4学习笔记(41)——ORB特征提取描述算法
- 什么是io?程序员该怎么理解io
- [区块链安全-链上分析]链上安全分析及相关POC编写
- CDR插件开发之CPG插件003 - 使用VS2019编译CDR类型库 VGCoreAuto.tlb
- using(){}的使用
- nodejs+express+mysql 之 简单的在线HTML编辑器
- 海康API——获取监控点预览取流URL,获取的rtmp流不能播放
- 解决itunes默认备份在C盘,C盘空间占用问题
- fcbf特征选择步骤_一文读懂 聚类特征选择