[toc]
0x00 前言简述
描述: 在当前云原生以及微服务流行的环境下,越来越多的开发者使用API接口实现数据的增删改查(CURD),将应用间的依赖解耦合,提高代码复用,便于水平扩展。所以为SZJX 😳 作为一名运开有必要进行相关规范的学习实践。
Q: 什么是API?
API (Application Programming Interface ,应用程序编程接口) 是一些预先定义的函数或者接口,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无须访问源码,或理解内部工作机制的细节。 简单的说,通过API请求接口我们可以实现任意数据操作,并且可以更加直观简约描述该请求的操作,更便于代码复用。
Q: API 服务器的设计规范和组合
我们要实现一个API 接口服务器时,需要考虑两个方面一个是API设计风格,另外一个是返回的数据类型。 此处以Go语言为例,在 API 开发中常见的组合是
REST + JSON
或者gRPC + Protobuf
,其中REST
和gRPC
即是API设计风格,而JSON
和Protobuf
则是请求返回的数据类型,当然我们也可以使用返回XML格式,上述组合是开发中常用(推荐),在实际开发中还需根据业务需要以及环境来选型。
此处,由于作者实际学习需要,本章主要介绍 RESTful API 设计规范,在后续文章中也会归纳总结 gRPC 设计规范。
0x01 RESTful API 接口简介
描述: RESTful API 是目前比较成熟的一套互联网应用程序的API设计理念,REST (REpresentational State Transfer
)表现层状态转移,由 Roy Fielding 在他的论文中提出。
REST 是一种软件架构风格不是技术框架, 实际上是一组架构约束条件和原则,当满足REST有一系列规范的 API 均可称为 RESTful API
RESTful 架构具有结构清晰、符合标准、易于理解以及扩展方便等特点,受到越来越多开发者喜爱并广泛应用在各类的网站上!
RESTful API 核心规范
- REST 中一切实体都被抽象成资源,每个资源有一个唯一的标识 URI ,所有的行为都应该是在资源上的 CRUD 操作
- REST 中是无状态的,即请求都包含了所有足够完成本次操作的依赖信息,服务器端无须依赖Session(
会话保持
),PS: 无状态对于服务端的弹性扩容是很重要的。 - REST 中天生和 HTTP 协议相辅相成,所以使用标准的HTTP协议方法 POST 、 DELETE 、 PUT 、 GET 方法来对应 REST 资源的增、删、改、查操作。
HTTP方法 | 行为操作 | API URL | 操作说明 |
---|---|---|---|
GET | 获取资源列表 | /users | 获取用户账号列表 |
GET | 获取一个具体的资源 | /users/admin | 获取admin账号的相关详细信息 |
POST | 创建一个新的资源 | /users/weiyigeek | 创建一个weiyigeek的用户账号 |
PUT | 以整体的方式更新一个资源 | /users/10000 | 更新用户ID为10000的账号 |
DELETE | 删除服务器上的一个资源 | /users/10000 | 删除id为10000的账号 |
0x02 RESTful API 接口设计
描述: 前面快速描述 RESTful API 接口规范,本节将实践根据规范来进行 RESTful API 设计,以及在实际开发中应该怎么做。
域名规范
描述: 应该尽量将API部署在专用域名之下,如果确定API很简单不会有进一步扩展,可以考虑放在主域名下。
# 方式1.独立域名(推荐)
https://api.weiyigeek.top
方式2.主域名后的目录
版本规范
描述: 应尽量使用版本( Versioning )和 应用的应用程序名称来规范。
# 方法1.将API的版本号以及该api应用的应用程序名称放入URL(推荐)
http://api.weiyigeek.top/app/1.0/foo
http://api.weiyigeek.top/app/1.1/foo方法2:将版本号放在HTTP头信息中,但不如放入URL方便和直观,Github采用这种做法。
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。
版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):
Accept: app.weiyigeek-top.foo+json; version=1.0
Accept: app.weiyigeek-top.foo+json; version=2.0
路径规范
描述 : 路径又称 “ 终点 “ ( endpoint ),表示 API 的具体网址,每个网址代表一种资源(resource)
使用名词
对于一个简洁结构,你应该始终用名词, 资源作为网址,只能有名词不能有动词 ,而且所用的名词往往与
数据库的表名对应
。
# 获取所有产品清单 GET /products # 获取产品4的信息 GET /products/4 # 更新产品4的信息 PATCH(或)PUT /products/4 # 新建产品 POST /products
#-------------------------------#
错误或者不建议的示例
获取所有端口
/getProducts
获取所有订单
/listOrders
订单1的所有回复(排序返回)
/retreiveClientByOrder?orderId=1
使用复数
API 中的名词应该使用复数,无论子资源或者所有资源。
# 获取单个产品
GET http://api.weiyigeek.top/AppName/1.0/products/1
# 获取所有产品
GET http://api.weiyigeek.top/AppName/1.0/products
请求操作规范
描述: 前面我们在讲解RESTful API 核心规范中说到对于资源的具体操作类型通常与HTTP请求方法对应,其中用的HTTP动词如下所示,其中括号里是对应的 SQL 命令。
HTTP动词
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
DELETE(DELETE):从服务器删除资源。
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
示例
# 获取所有产品
GET http://api.weiyigeek.top/AppName/1.0/products
# 创建单个产品
POST http://api.weiyigeek.top/AppName/1.0/products
# 更新单个产品
PUT http://api.weiyigeek.top/AppName/1.0/products/1
# 删除单个产品
DELETE http://api.weiyigeek.top/AppName/1.0/products/1
当然为了安全你也可以只使用GET(查询操作)与POST请求(关键性操作),例如
更新产品ID为1的数据
POST http://api.weiyigeek.top/AppName/1.0/products/1/update
删除产品ID为2的数据
过滤信息规范
描述: 如果API返回的数量很多,服务器不可能都将它们返回给用户,此时 API 应该提供参数,过滤返回结果。
# 指定返回记录的数量
GET http://api.weiyigeek.top/AppName/1.0/products?limit=10指定返回记录的开始位置
GET http://api.weiyigeek.top/AppName/1.0/products?offset=10
指定第几页以及每页的记录数
GET http://api.weiyigeek.top/AppName/1.0/products?page=2&per_page=100
指定返回结果按照哪个属性排序以及排序顺序
GET http://api.weiyigeek.top/AppName/1.0/products?sortby=name&order=asc
指定筛选条件
GET http://api.weiyigeek.top/AppName/1.0/products?animal_type_id=1
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。
比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
状态码规范
描述: 服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的 HTTP 动词)。
状态码 | 适用请求 | 备注 |
---|---|---|
200 OK | [GET] | 服务器成功返回用户请求的数据 |
201 CREATED | [POST/PUT/PATCH] | 用户新建或修改数据成功。 |
202 Accepted | [] | 表示一个请求已经进入后台排队(异步任务) |
204 NO CONTENT | [DELETE] | 用户删除数据成功。 |
400 INVALID REQUEST | [POST/PUT/PATCH] | 用户发出的请求有错误,服务器没有进行新建或修改数据的操作 |
401 Unauthorized | [] | 表示用户没有权限(令牌、用户名、密码错误)。 |
403 Forbidden | [] | 表示用户得到授权(与401错误相对),但是访问是被禁止的。 |
404 NOT FOUND | [] | 用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 |
406 Not Acceptable | [GET] | 用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 |
410 Gone | [GET] | 用户请求的资源被永久删除,且不会再得到的。 |
422 Unprocesable entity | [POST/PUT/PATCH] | 当创建一个对象时,发生一个验证错误。 |
500 INTERNAL SERVER ERROR | [*] | 服务器发生错误,用户将无法判断发出的请求是否成功。 |
返回与错误处理规范
描述: 在服务器返回的数据格式,应该尽量使用 JSON 避免使用 XML,在实际开发中常常也是使用 JSON。
- 标注标准返回结果,实际开发通常返回是200,错误是 0
// # 正常返回
{
"code": 200,
"message": "服务器成功返回用户请求的数据.",
"data": {
"name": "WeiyiGeek",
"blog": "blog.weiyigeek.top"
}
}
// # 异常返回
{
"code": 0,
"message": "服务器未能成功返回用户请求的数据.",
"data": {}
}
- 定制标准化的返回结果
{
"code": 10002,
"message": "User data operation completed.",
"data": {}
}
- 定制标准化的错误返回结果 (例如,code 大于等于200)
{
"code": 20002,
"error": "The server failed to successfully return the data requested by the user."
}
0x03 RESTful API 服务器运行流程
描述: 此处以Go语言的Web框架Gin模块包为例设计的API服务器,其启动运行流程大致如下(开发自定义请根据实际情况进行设计):
- 1.命令启动API服务器后,其首先加载配置文件,根据配置做后面的处理工作。
- 2.通常会将日志相关的配置记录在配置文件中,在解析完配置文件后加载日志包初始化函数,来初始化日志实例,供后面的程序调用。
- 3.然后初始化数据库实例,建立数据库连接,供后面对数据库的 CRUD 操作使用。
- 4.设置HTTP请求,通常包括 3 方面的
注册路由
、设置 Header
、注册中间件
- 5.然后加载设置的路由以及健康检查预警。
- 5.然后调用 net/http 包的 ListenAndServe() 方法启动 HTTP 服务器(即启动绑定监听接口地址和端口)。
- 6.启动 HTTP 端口之前,程序会 go 一个协程,来ping HTTP 服务器的
/sd/health
接口,如果程序成功启动,ping 协程在timeout 之前会成功返回,如果程序启动失败,则 ping协程最 timeout,并终止整个程序
WeiyiGeek.API 服务器运行流程图
0x04 RESTful API 项目结构
在 Go API 项目中通常会包括很多功能,例如 Makefile 文件(编译文件)、配置文件目录、 RESTful API 服务器的 handler 目录、 model 目录、工具类目录、 vendor 目录,以及实际处理业务逻辑函数所存放的service 目录。
上述功能代码在结构中有列出,新加功能时将代码放入对应功能的目录/文件中,可以使整个项目代码结构更加清晰,非常 有利于后期的查找和维护 。
# Linux
mkdir -vp ./config/{tls,conf} ./docs/swagger ./handler/sd/user ./model ./pkg/{errno,version} ./router/middleware ./service ./util ./vendorwindows
mkdir -vp ./config/tls ./config/conf ./docs/swagger ./handler/sd/user ./model ./pkg/errno ./pkg/version ./router/middleware ./service ./util ./vendor
WeiyiGeek.API 项目结构图