类似于阿里巴巴的开发规范或者谷歌的开发规范,API设计是目前很多场景下的基本功,所以这里给出一个笔者的最佳实践。
REST,即Representational State Transfer的缩写,关于RESTful架构,可以参考《架构之美》中的定义。
GET: 获取资源
POST: 更新资源
PUT: 更新资源
DELETE: 删除资源
API与客户端通信的协议,应该尽量使用HTTPS。除此之外,白名单机制、VPN可以提高更高的安全性。遵循“所有人应该知道他所需要完成工作必备的最少的知识”的原则。而一个接口,只有在访问者必须使用它时,才告诉这个访问者。
应该将API部署到专用域名之下,好处显而易见。
示例:https://api.groupname.domain.io/
更好的实践是将服务分组,并且根据情况进行必要的层次和分组(group)。这里的group可以包含事业部,也可以包含不同的客户,更可以包含不同的层次。
规范:https://api.[service-group].domain.io/
基本的URL,在此文中指的是除了服务拆分之外的URL,这里的最佳实践是可以包含环境、版本、分类、层次等,但是一个基本的URL,已经能决定除了接口或者服务粒度以外的所有事情,或者说可以决定由一个作战单元(个人或者小的敏捷团队)日常维护的工作内容了。
示例:https://api-dev.groupname.domain.io/mobile/v1/comment/[...]
整体服务架构的划分原则不在此文档讨论范围之内,而一个最佳实践是在domain当中就对环境作区分。
规范:https://api-[env-name].groupname.domain.io/
应该将API的版本号放入URL合适的层次。注意这里的Version既不表示客户端的版本,也不表示服务器中服务对应的版本,而是特指该接口的版本。一般用来处理对接口进行升级的情况。
规范:https://api-[env-name].groupname.domain.io/mobile/[version]/
提供以下原则供参考:路径表示API的具体URL,每一个URL唯一的表示一种资源,所以网址中不应该有动词,只应该有名词,而且所用的名词往往与代表的对象名称对应,一般来说是某一种记录的集合,所以API名词当中应该使用复数。
以下括号中对应SQL的动词:
最佳实践
一般有如下建议:
API应该提供参数,过滤返回结果。因为服务器端某个资源数量可能很多,比如用户的订单数、全国的酒店数等。过滤的语义应该包括对数据集合的过滤、排序、选择和分页等功能。
最佳实践
如果状态码不是正确的返回,就应该返回出错信息,尽量使用详细的错误信息,一个好的实践是出错信息应该包含:
GET /collection:返回资源列表
GET /collection/resource:返回单个对象
POST /collection/resource:返回新生成的对象
PUT /collection/resource:返回完整的更新后的资源对象
PATCH /collection/resource:返回完整的更新后的资源对象
DELETE /collection/resource:返回一个空文档
HATEOAS(Hypermedia as the Engine of Application State),也即超媒体作为应用状态的引擎。超媒体API很可能是RESTful API设计的未来,它们实际上是一个非常惊人的概念,可以追溯到HTTP和HTML的工作原理。我们可以使用HATEOAS in Spring来构建Hypermedia APIs,而在此之前约定更加重要。
一个好的Hypermedia范例是:https://api.github.com/
{
"current_user_url": "https://api.github.com/user",
"current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
"authorizations_url": "https://api.github.com/authorizations",
"code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
"commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
"emails_url": "https://api.github.com/user/emails",
"emojis_url": "https://api.github.com/emojis",
"events_url": "https://api.github.com/events",
"feeds_url": "https://api.github.com/feeds",
"followers_url": "https://api.github.com/user/followers",
"following_url": "https://api.github.com/user/following{/target}",
"gists_url": "https://api.github.com/gists{/gist_id}",
"hub_url": "https://api.github.com/hub",
"issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
"issues_url": "https://api.github.com/issues",
"keys_url": "https://api.github.com/user/keys",
"label_search_url": "https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}",
"notifications_url": "https://api.github.com/notifications",
"organization_url": "https://api.github.com/orgs/{org}",
"organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
"organization_teams_url": "https://api.github.com/orgs/{org}/teams",
"public_gists_url": "https://api.github.com/gists/public",
"rate_limit_url": "https://api.github.com/rate_limit",
"repository_url": "https://api.github.com/repos/{owner}/{repo}",
"repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
"current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}",
"starred_url": "https://api.github.com/user/starred{/owner}{/repo}",
"starred_gists_url": "https://api.github.com/gists/starred",
"user_url": "https://api.github.com/users/{user}",
"user_organizations_url": "https://api.github.com/user/orgs",
"user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
"user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}
标准的请求定义当中有很多最佳实践,比如Content-Type等。好的实践是我们尽早用较小的代价将Content-Type、language等加入设计当中,可以避免后续很多问题。
认证的时候取决于API的使用者和生产者之间的关系以及需要保护的程度,目前此处的最佳实践是采用OAuth 2.0当中合适的模式来构建。
使用 Swagger API+JSON 进行文档管理和信息描述,定义一个标准的、语言无关的、供人和计算机理解服务的文档,类似于SOAP当中的WSDL。