RESTful API 设计指南与最佳实践 | RESTful API Design Guide and Best Practices
面向开发者的REST架构实践指南,涵盖HTTP方法、资源定义、幂等性原则与状态管理
什么是RESTful API?
REST (Representational State Transfer) 是一种软件架构风格,用于设计网络应用程序接口。REST本身并没有创造新的技术、组件或服务,而是利用Web的现有特征和能力,更好地使用现有Web标准中的准则和约束。虽然REST受Web技术影响深远,但理论上REST架构风格并不局限于HTTP协议,只是目前HTTP是实现REST的主要实例。本文主要描述基于HTTP实现的RESTful API设计原则。
REST架构的核心概念
REST架构基于以下几个核心概念构建:
资源与URI
统一资源接口
资源的表述
资源的链接
状态的转移
1. 资源与URI (Uniform Resource Identifier)
资源定义
任何事物,只要有被引用的必要,它就是一个资源。
每个资源需要有唯一标识符,在Web中这个唯一标识就是URI。
URI既可视为资源的地址,也可视为资源的名称。
URI设计原则
URI的设计应遵循以下原则:
可寻址性:每个资源都应有明确的地址
自描述性:URI本身应提供资源信息
直觉关联性:形式上应给人直观的关联
URI设计最佳实践
使用下划线(_)或连字符(-)提高URI可读性
使用斜杠(/)表示资源的层级关系
使用问号(?)过滤资源
使用逗号(,)或分号(;)表示同级资源关系
2. 统一资源接口 (Uniform Interface)
无论资源类型如何,都应通过统一接口进行访问。RESTful API应使用标准HTTP方法,并遵循这些方法的语义。
幂等性原则
在编程中,幂等操作指任意多次执行所产生的影响与一次执行的影响相同。在REST API中:
幂等方法:GET、HEAD、PUT、DELETE
非幂等方法:POST
HTTP方法语义
GET
特性:安全且幂等
用途:获取资源表示
缓存:变更时获取表示(支持缓存)
状态码:
200 (OK) - 成功返回资源
204 (No Content) - 资源有空表示
301 (Moved Permanently) - 资源URI已更新
303 (See Other) - 其他情况(如负载均衡)
304 (Not Modified) - 资源未更改(缓存)
400 (Bad Request) - 指代坏请求(如,参数错误)
404 (Not Found) - 资源不存在
406 (Not Acceptable) - 服务端不支持所需表示
500 (Internal Server Error) - 通用错误响应
503 (Service Unavailable) - 服务端当前无法处理请求
POST
特性:不安全且不幂等
用途:
使用服务端管理的(自动产生)的实例号创建资源
创建子资源
部分更新资源
如果没有被修改,则不过更新资源(乐观锁)
状态码:
200 (OK) - 如果现有资源已被更改
201 (Created) - 如果新资源被创建
202 (Accepted) - 已接受处理请求但尚未完成(异步处理)
301 (Moved Permanently) - 资源的URI被更新
303 (See Other) - 其他(如,负载均衡)
400 (Bad Request) - 指代坏请求
404 (Not Found) - 资源不存在
406 (Not Acceptable) - 服务端不支持所需表示
409 (Conflict) - 通用冲突
412 (Precondition Failed) - 前置条件失败(如执行条件更新时的冲突)
415 (Unsupported Media Type) - 接受到的表示不受支持
500 (Internal Server Error) - 通用错误响应
503 (Service Unavailable) - 服务当前无法处理请求
PUT
特性:不安全但幂等
用途:
用客户端管理的实例号创建一个资源
通过替换的方式更新资源
如果未被修改,则更新资源(乐观锁)
状态码:
200 (OK) - 如果已存在资源被更改
201 (Created) - 如果新资源被创建
301 (Moved Permanently) - 资源的URI已更改
303 (See Other) - 其他(如,负载均衡)
400 (Bad Request) - 指代坏请求
404 (Not Found) - 资源不存在
406 (Not Acceptable) - 服务端不支持所需表示
409 (Conflict) - 通用冲突
412 (Precondition Failed) - 前置条件失败(如执行条件更新时的冲突)
415 (Unsupported Media Type) - 接受到的表示不受支持
500 (Internal Server Error) - 通用错误响应
503 (Service Unavailable) - 服务当前无法处理请求
DELETE
特性:不安全但幂等
用途:删除资源
状态码:
200 (OK) - 资源已被删除
301 (Moved Permanently) - 资源的URI已更改
303 (See Other) - 其他,如负载均衡
400 (Bad Request) - 指代坏请求
404 (Not Found) - 资源不存在
409 (Conflict) - 通用冲突
500 (Internal Server Error) - 通用错误响应
503 (Service Unavailable) - 服务端当前无法处理请求
HTTP方法扩展
统一接口并不阻止你扩展方法,只要方法对资源的操作有着具体的、可识别的语义即可,并能够保持整个接口的统一性。
像WebDAV就对HTTP方法进行了扩展,增加了LOCK、UPLOCK等方法。而GitHub的API则支持使用PATCH方法来进行issue的更新,例如:
注意 ⚠️ 像PATCH这种不是HTTP标准方法的,服务端需要考虑客户端是否能够支持的问题。
URI设计指导
统一资源接口要求使用标准的HTTP方法对资源进行操作,所以URI只应该来表示资源的名称,而不应该包括资源的操作。
通俗来说,URI不应该使用动作来描述。
3. 资源的表述 (Representation)
确切的说,客户端获取的只是资源的表述而已。资源在外界的具体呈现,可以有多种表述(或成为表现、表示)形式,在客户端和服务端之间传送的也是资源的表述,而不是资源本身。例如文本资源可以采用html、xml、json等格式,图片可以使用PNG或JPG展现出来。
资源的表述包括数据和描述数据的元数据,例如,HTTP头"Content-Type" 就是这样一个元数据属性。
客户端可以通过Accept头请求一种特定格式的表述,服务端则通过Content-Type告诉客户端资源的表述形式。
API版本管理
在URI里边带上版本号:
http://api.example.com/1.0/foo
http://api.example.com/1.2/foo
http://api.example.com/2.0/foo
表述格式区分
使用URI后缀来区分表述格式。当服务器不支持所请求的表述格式,那么应该怎么办?若服务器不支持,它应该返回一个HTTP 406响应,表示拒绝处理该请求。
4. 资源的链接 (Hypermedia)
下面展示的是GitHub获取某个组织下的项目列表的请求,可以看到在响应头里边增加Link头告诉客户端怎么访问下一页和最后一页的记录。而在响应体里边,用url来链接项目所有者和项目地址。
上面的例子展示了如何使用超媒体来增强资源的连通性。很多人在设计RESTful架构时,使用很多时间来寻找漂亮的URI,而忽略了超媒体。所以,应该多花一些时间来给资源的表述提供链接,而不是专注于"资源的CRUD"。
5. 状态的转移 (State Transfer)
无状态通信原则,并不是说客户端应用不能有状态,而是指服务端不应该保存客户端状态。
应用状态与资源状态
状态应该区分应用状态和资源状态,客户端负责维护应用状态,而服务端维护资源状态。
但有时候我们会做出违反无状态通信原则的设计,例如利用Cookie跟踪某个服务端会话状态,常见的像J2EE里边的JSESSIONID。
这意味着,浏览器随各次请求发出去的Cookie是被用于构建会话状态的。
当然,如果Cookie保存的是一些服务器不依赖于会话状态即可验证的信息(比如认证令牌就是 token),这样的Cookie也是符合REST原则的。
应用状态的转移
"会话"状态不是作为资源状态保存在服务端的,而是被客户端作为应用状态进行跟踪的。
RESTful架构与传统架构对比
RESTful架构与传统的RPC、SOAP等方式在理念上有很大的不同。
总结
RESTful API设计是一门艺术,需要在实践中不断完善。遵循本文介绍的设计原则,能够帮助你构建出更加清晰、易用、可扩展的Web API。记住:好的API设计应该是对资源的抽象,而不是对接口的堆砌。
Last updated
Was this helpful?