Restful风格接口设计从入门到埋坑

API接口设计实践

查看Restful风格各种场景下接口示例请直接跳转接口设计部分

背景

前后端开发中,后端需要为前端提供接口。那么我们如何来提供一套相对优雅的接口设计实践?

我相信许多同学都要命名困难综合征,拍脑袋的给出接口又很难形成良好的实践,那尤其如果是多人共同开发的工程,很容易就形成各种风格各立的接口,增加了一丢的不易维护。那想当然我也是其中的一员…所以这里也是给出自己的一份实践和参考。

前后端接口设计通常是Http接口。Http接口设计目前比较流行的是Restful风格接口设计。

Restful风格

得益于Http协议的相对规范与通用,有了Restful风格的接口设计。Restful的思路是将请求视为对资源的操作,用HttpMethod方法表示操作,Path表示资源路径。下面我们会大概看一下各种场景下Restful风格实践的一种思考。

Http接口设计

关于Http接口设计,我们基本遵循Restful风格,当然Restful风格不是银弹,有些情况下它并不适用,很容易挠头秃。这里我主要参考Java开发手册》和谷歌的API设计指南

设计约定

  1. 【强制】前后端交互的 API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响应体。

    1. 协议:生产环境必须使用 HTTPS
    2. 路径:每一个 API 需对应一个路径,表示 API 具体的请求地址:
      1. 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。
      2. URL 路径不能使用大写,单词如果需要分隔,统一使用~~下划线~~短横线1
      3. 路径禁止携带表示请求内容类型的后缀,比如".json",“.xml”,通过 accept 头表达即可。
    3. 请求方法:对具体操作的定义,常见的请求方法如下:
      1. GET:从服务器取出资源。
      2. POST:在服务器新建一个资源。
      3. PUT:在服务器更新资源。
      4. DELETE:从服务器删除资源。
    4. 请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type2
  2. 【强制】服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,errorCodeerrorMessage、用户提示信息四个部分。

    1. 说明:四个部分的涉众对象分别是浏览器、前端开发、错误排查人员、用户。
    2. 用户的提示信息要求:简短清晰、提示友好,引导用户进行下一步操作或解释错误原因,提示信息可以包括错误原因、上下文环境、推荐操作等。
    3. errorMessage:简要描述后端出错原因,便于错误排查人员快速定位问题,注意不要包含敏感数据信息。
    4. 错误码不能直接输出给用户作为提示信息使用。
  3. 【强制】HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。

    说明:不同浏览器对于 URL 的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048字节是取所有浏览器的最小值。

  4. 【强制】HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出错。

    说明:nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。

  5. 【参考】在接口路径中不要加入版本号,版本控制在 HttpHeader 头信息中体现,有利于向前兼容。

    说明:当用户在低版本与高版本之间反复切换工作时,会导致迁移复杂度升高,存在数据错乱风险。

这里基本规范了接口设计及命名,还包含异常返回。但是我们也可以看到这种API对操作单个资源比较友好,而我们很多场景会有批量、分页等,设置有些很难用对资源的操作来表达。那这里我们可以结合谷歌的API设计来补充这些场景。

接口设计

这里我们以最常见的用户资源为例,以复数表达资源为users3

请求描述URI示例备注
创建一个用户POST /usersPOST /users
{“name”:“test”}
通过body传参
查询某个用户GET /users/{id}GET /user/1GET语义不支持传参body
修改某个用户PUT /users/{id}PUT /users/1
{“name”:“test”}
通过body传参
删除某个用户DELETE /users/{id}DELETE /user/1DELETE语义不支持传参body
分页列取用户GET /usersGET /users?name=test比较通用的列取查询,比如按用户某个属性分页列取

我们可以看到Restful的风格可以提供的标准接口命名就是上面5种,基本上都是以一个资源的角度来设计的。但通常我们也会有些批量操作或者复杂的查询,更常见的则是我们可能会需要更细粒度的修改操作,比如启用/停用用户。

这些在谷歌API设计中视作为非标准方法,此时通常来说我们不可避免的会在URI中用动词来区分操作,谷歌推荐了一种使用":"分隔动作的风格。

请求描述URI示例备注
批量创建用户POST /users:mpost
[{“name”:“test”}]
批量查询用户(按id)GET /users:mgetGET /users:mget?ids=1,2,3性能要求使用批量接口,自定义方法
限制批量查询条数!最多2048字节
批量修改用户PUT /users:mput
[{“id”:1}]
PUT /users会有歧义,批量还是全部?
批量删除用户DELETE /users:mdel?ids=1,2,3DELETE /users会有歧义,批量还是全部?
限制批量查询条数!最多2048字节
复杂查询用户GET /users:serarchGET /users:serarch?orgIds=1,2
非简单属性修改(停用用户)PUT /users/{id}:disablePUT /users/1:disable标准的资源方法不合适表达语义,
如重启机器、发送邮件
(通常体现出状态、流程的变化)
批量清空用户DELETE /users:clearDELETE /users:clear自定义批量的一种case
移动用户(组织下)PUT /orgs/1users/{id}:movePUT /orgs/1users/1:move
{“orgId”:“2”}
存在父子资源关系时的移动到另一个父级下

以上,批量的接口命名险些让我头秃…这里使用multi+post/get/put/delete命名。


  1. URL命名通常有三种,驼峰命名法(serverAddress),蛇形命名法(server_address),脊柱命名法(server-address)。由于URL是大小写敏感的,如果用驼峰命名在输入的时候就要求区分大小写,一个是增加输入难度,另外也容易输错,报404。蛇形命名法用下划线,在输入的时候需要切换shfit,同时下划线容易被文本编辑器的下划线掩盖。所以建议使用脊柱命名法(server-address)。Java开发手册建议使用下划线,而GoogleAPI建议使用驼峰。 ↩︎

  2. 默认 application/json;charset=UTF-8 ↩︎

  3. 两份文档都推荐使用复数形式,在写这篇文档之前我个人的习惯一直是单数(可能是受到表设计的影响)。不过综合比较各种实现和说法来看,我也建议使用复数形式表示资源(集合)。关于使用单数有另外2个场景:1. 单例资源或无复数形式,比如值对象或者1:1的关系;2. 单数表示单个资源,复数表示复数资源。2不建议这么去玩,我们这里需要一个语义上的资源概念。 ↩︎


版权声明:本文为theskyzero原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。