【没时间看,记住这3点】
各位,如果没时间看,记住3点:一是 REST 以资源为中心设计的 API,数据架构和数据模型设计对 REST 接口质量有很大影响;二是遇事不决就用 POST;三是参考业界的 REST API,如 github,elasticsearch。
【REST 背景】
2000 年,互联网兴起数年,企业系统广泛流行通过 HTTP 实现远程调用,代替 EJB,当时基于 HTTP 远程调用,无论 URL 的设计,参数命名,以及响应都没有统一的规范。Roy Fielding 针对此问题,提出了表示状态转移(Representational State Transfer),简称 REST,作为一种设计 web 服务的体系结构方法。REST 是目前最流行的 HTTP API 设计规范,用于 Web 接口的设计。
REST 的核心思想就是,客户端发出的指令都是“标准动词 + 操作资源”的结构。比如,`GET /orders/1` 这个命令,`GET` 是动词,`/orders/1` 是宾语,这里的 1 是指订单编号为 1 的订单。
如下是一个 REST 请求:
GET https://xxxx/orders/1 HTTP/1.1 Accept: application/json响应:
HTTP/1.1 200 OK { "code":"SUCCESS" "messsage":"成功" "data":{"orderId":1,"orderValue":99.90,"productId":1,"quantity":1} }这里的标准动词是 HTTP 规范提供的标准,如上面的 get,其他动作还有 post、delete,将会在后面描述。
相比于 HTTP 远程调用,REST 是以资源作中心构建的远程调用服务,这里的资源指的是业务实体,业务实体来自于数据架构。一个良好的 REST API 设计,离不开良好的数据架构。
以 GIT 提供的服务为例子,资源包括了:仓库、用户、Issues、Release、Team。以物联网为例子,资源包括了:设备、用户、家庭、属性、告警、OTA、命令。
【REST 和其他协议对比】
在上一节,比较过二进制协议和 HTTP 的区别,建立在 HTTP 之上的 REST,具备 HTTP 的优点:一是语言独立,所有语言都支持 HTTP 协议,也提供 REST 风格的客户端接口;二是平台独立,所有的系统都支持 HTTP;三是可观测性强,通过 HTTP 代理服务器,以及良好的 REST 接口定义,可以让系统之间调用可观测,可统计。
基于 HTTP 协议的除了 REST 外,还有非常流行的 SOAP,简单区分俩种的区别:
| REST | SOAP |
|---|---|
| 核心:资源 | 服务(方法),像传统分布式调用 Corba,EJB 的的 HTTP 版本 |
| 量级:轻便,可选约束 | 重量级,必须约束 |
| 适用范围:任何系统,服务之间;终端和系统之间 | 系统之间,如人力系统和财务系统 |
【REST 最佳实践】
本节介绍 REST 的设计最佳实践,它除了上一节的分布式 API 通用的最佳实践外,REST API 还有特定的设计原则,总结为下表内容和表后的重点说明:
| 设计原则 | 描述 |
|---|---|
| URL | URL 必须定义了资源,比如 /books/1978 |
| 命名规范 | 驼峰命名方法,蛇形命名法,烤肉串命名法。比如对于显示名称 display name。分别对应了 displayName,display_name,dispaly - name。REST 的 URL 通常使用烤肉串命名法,入参和出参数使用 Java 常用的驼峰命名法 |
| 资源名称 | API 的核心是操作是 WEB 资源或者业务实体,因此需要在这之前定义好这些名称,这些名称在数据架构或者更早的数据架构前已经定好。比如用户中心提供的 REST 接口查询用户的绑定设备 GET /users/devices 。如果缺少标准名称,另外一个接口查询设备状态 GET /devs/customer 。这让 API 使用者有困惑,他可能认为 devices 和 devs 可能是物联网的俩种设备资源名称通常以复数形式体现 |
| 资源层级 | 当资源必须通过层级获取的时候,可以用 / 表达层级,比如 /orders/{orderId}/items,建议层级不超过 3 层。 |
| 标准字段 | 定义常见的标准字段,如 requestId,token,createTime,orderBy,displayName,startDate,endDate 等。其他行业特定的字段应该基于数据架构的概念模型来定义,比如物联网中的 device,shadow,电商中的 coupon,order,sku |
| 开闭区间 | 表示范围,需要统一规定是否包含临界值,Java 通常使用[、),起始值闭区间,结束值为开区间,推荐使用这种约定 |
| 使用单位后缀 | 比如优先使用 maxByte 代替 max,除非有上下文指示返回的单位。关于时间,精确到秒,应该使用 time,精确到天,应该使用 date 后缀。 |
| 名字缩写 | 软件开发者常用的缩写可以用在 API 上,如 id,config,电商中的 Sku。 |
| 属性 | class 是 java 的关键字,因此在设计 API 的时候需要避免参数中使用此关键字,这主要考虑到序列化和反序列化时候,这类关键字无法被实现;is 开头的属性有可能在反序列化中失败:isCreate,有的序列化成 isCreate,有的序列化成 create ;需要明确属性不存在和属性值为 null 是同一个含义 明确空集合和 null 是同一个含义 ;客户端是否支持浮点数,以 JS 为例子,[不支持 Java 返回的过长的数字](https://blog.csdn.net/u010028869/article/details/86563382) |
| 版本号 | 通常使用 version 表示 api 的版本,比如 /gms/v1.0/devices,建议 REST URL 总是包含 version。另外可选的是在 HTTP Header 中包含 version。比如 github api 推荐把 version 放到 header 里 |
下表列出了 Github REST API:
| REST 接口 | 说明 |
|---|---|
| /user/repos | 获取当前用户的所有仓库 |
| /repos/{owner}/ | 查询一个仓库信息 |
| /repos/{owner}/{repo}/releases | 来自 github api,返回代码库的所有 release |
| /repos/{owner}/{repo}/releases/ | 来自 github api,返回代码库的指定的 release 信息 |
下列情况是错误的 URL 例子:
| REST 接口 | 正确 | 错误分析 |
|---|---|---|
| /book | /books | 通常使用复数,除非查询单个对象 |
| /user/create | /user | URL 不应该包含动词 |
| /users/{id}/books.json | /users/{id}/books | 无需指定返回的格式,通常默认是 json,如果需要其他格式,可以通过媒体类型来指定,比如 **application/xml** |
思考:查看你系统的的 REST API,看看有没有不符合 RESTAPI 设计原则的。
【标准方法命名】
HTTP 提供了标准的动作 GET、PUT、POST、DELETE 等:GET 表示读取;POST 表示新建;PUT 表示创建或者更新;PATCH 表示更新(Update),通常是部分更新;DELETE 表示删除(Delete)。
| URL | 动作 | 含义 |
|---|---|---|
| /users | GET | 查询所有用户 |
| /users/1 | GET | 查询 id 为 1 的用户 |
| /users | POST | 创建新的用户 |
| /users/1 | PUT | 创建或者更新用户,id 为 1 |
| /users/1 | DELETE | 删除用户 1 |
| /users | DELETE | 删除所有用户 |
【范围查询】
规定使用 offset 和 limt 作为查询参数,如果数据来源是大数据,通常使用 cusor 方式查询,翻页范围使用 cusor 和 limit 参数俩个,这里的 cusor 通常是递增主键 id。
【HTTP Stauts】
在开发 API 时候,对于 REST 的响应结果,还需要进一步规范如下 API 说明,通常用如下:200 表示成功;400 表示客户端错误 ,其中 400 服务器不理解请求,如参数错误,401 未通过身份验证,403 未授权的访问资源,404 访问资源不存在;500 表示服务端错误。
关于 HTTP Status 定义,参考[HTTP 响应状态](https://developer.mozilla.org/zh - CN/docs/Web/HTTP/Status)。
如果这些 Status Code 不足以表达更明确的含义,建议在响应体的 JSON 里包含 code 进一步明确错误含义,比如 code 为 PARAMETER_ERROR 表示参数校验错误。message 通常用于补充 code 含义。
HTTP/1.1 200 OK Content - Type: application/json { "code":"SUCCESS" "messsage":"库存查询成功" "data":{ } }错误响应:对于基于 HTTP 协议的 API,可以充分利用 HTTP STATUS 来表示 API 是否成功,STATUS 304 表示授权错,STATUS 500 表示内部失败,等等,使用 HTTP STATUS 也有利于其他系统,如网关或者监控系统对错误进行观测。
HTTP/1.1 500 ? Content - Type: application/json { "code":"PARAMETER_ERROR" "messsage":"操作失败," "data":{ } }使用 HTTP Status,还是使用响应的 Body 中的 code 来指示 REST 的响应结果,屈居于俩个因素:一是 HTTP Status 仍然能粗略的表达 REST 响应状态,屈居于客户端是否对状态更详细的要求,比如 HTTP 500 表示服务器内不错误,对于大多数电商和企业应用系统,是不足以表达的;二是可观测性,如果已经搭建好的有可观测系统,且 REST 返回结果能被客观性系统收集到,则可以使用 CODE 来指示结果。 如果此时搭建的有 NGINX 的日志监控,HTTP Status 也能粗略的统计到 REST 请求结果。
如果 API 实现异步调用,需要通过文档向调用者说明,从哪里可以获取异步执行结果。或者提供一个新的 API,用户可以通过 reqeust_id,查询异步调用结果。或者服务端返回一个查询 ID,用户可以用此 ID 查询结果。
或者对于 REST 请求,也可以在响应中包含一个 URL,供用户查询。如下一个成功响应:
HTTP/1.1 200 OK Content - Type: application/json { "code":"ASYNC - SUCCESS" "messsage":"成功" "data":{ "url":"/device/ECD0017R1/status/344fer55656563" } }无论是成功或者失败,都应该包含 code 和 message,code 是成功或者错误的 CODE 定义,message 是用户可阅读的的消息。
对于响应的 JSON,还有如下设计建议:一是需要明确的区分其属性是否为 null,或者不存在;二是返回的空集合,使用[],而不是 null,或者不存在;三是返回的浮点数或者长整形,如果客户端是 JS,需要注意到有可能 JS 不支持,这情况下改成字符串。
【总有例外】
本节包含了通常 REST API 最佳实践,有其他资料对 API 最佳实践有更严格的规定,架构师需要衡量是否采用这些严格规定,本文列举了几个需要衡量的点:一是充分使用 HTTP Status Code,有些 REST 规范要求使用更多的 HTTP Status Code 表示,比如对于客户端错误,采用 403 表示权限不足,404 表示访问资源不存在使用。参考 rfc9110 了解更多 status 含义。本书建议使用 200,400,以及 500 即可,更详细的错误信息放在 code 里;二是 REST API 的 GET 查询不推荐携带 Body。允许 GET with Body,即允许 GET 查询携带 Body。这是因为可能查询参数结构较为复杂,比如 ES 提供的 /_search, 其 body 就是一个 JSON 格式。再比如,通常客户端 JS 框架会把页面查询条件的表单序列化成 JSON。因此通常建议使用 POST 请求,且 URL 路径上增加查询动作,比如 /books/search;三是删除通常是用 DELETE,如果是按照条件删除,可以使用 POST,比如 ES 中的按照查询条件删除 POST /my - index - 000001/_delete_by_query;四是 REST API 大部分实践都要求参数是蛇行命名。本书建议,蛇行命名,以可以像 Java 那样驼峰命名。
通常,基于 GET 请求的 REST,意味着通过 URL 分享此 REST 接口,其结果易于被客户端或者代理缓存,支持重定向等功能,如果 GET 支持 BODY,那必须确认 HTTP 代理或者是 WEB 框架是否支持。
【REST 服务成熟度模型】
个人觉得比较扯淡,也不建议企业按照 REST 成熟度模型构建服务。
【引用】
如何定义 REST 接口,还可以参考业内的一些实践,比如 Github REST API,ElasticSerch REST API。
作者博客 [Blog | 闲大赋];原始文章;Github REST 实践;GitHub 例子;Elastic Search 例子;推荐:阮一峰:RESTful API 最佳实践;推荐: **Zalando RESTful API and Event Guidelines**;推荐 微软 [RESTful web API design ];博客 [API Design Patterns for REST]。