Microsoft REST API Guidelines

20th August 2020 at 2:19pm
HTTP: API Design

微软在 GitHub 上维护了一份自家 REST API Guidelines,我看的是 2.3 版本。

我觉得这份材料并不一定值得一看:

  • Delta 部分设计得比较鸡肋,感觉为了通用而太过简单,难以实际应用
  • 写 Push notifications via webhooks 写了很长篇福,但是很可能你并不想了解这块内容
  • RESTful 之父 Roy Fielding 说 这并不是一个 RESTful API guidelines,而仅仅是一个 HTTP API guidelines
    • 有评论说,写成 Hypermedia API 更佳
    • 我搞不懂 RESTful 是啥

根据我个人的知识储备,我觉得值得一看的章节:

  • 7.10.2 Error condition responses
  • 9 Collections
  • 16 Naming guidelines

笔记

总体可用性考量

5 Taxonomy 描述了哪些错误应该被纳入总体可用性(overall availability)考量。比如:

  • Error 类,是由客户端引起(比如 BadRequest 等),不纳入总体可用性考量
  • Fault 类,Server 引起的错误,纳入考量
  • Long Running API,用户发起时 Server 会回 2XX 表示操作开始执行,但是一旦后续操作失败了,也应该纳入考量

幂等(idempotent)

From http://www.restapitutorial.com/lessons/idempotency.html:

From a RESTful service standpoint, for an operation (or service call) to be idempotent, clients can make that same call repeatedly while producing the same result. In other words, making multiple identical requests has the same effect as making a single request. Note that while idempotent operations produce the same result on the server (no side effects), the response itself may not be the same (e.g. a resource's state may change between requests).

一个操作如果是幂等的,那么客户端做多次调用时都会得到一样的结果,比如 GET 请求。

MethodDescriptionIs Idempotent
GETReturn the current value of an objectTrue
PUTReplace an object, or create a named object, when applicableTrue
DELETEDelete an objectTrue
POSTCreate a new object based on the data provided, or submit a commandFalse
HEADReturn metadata of an object for a GET response. Resources that support the GET method MAY support the HEAD method as wellTrue
PATCHApply a partial update to an objectFalse
OPTIONSGet information about a request; see below for details.True

PUT vs PATCH

PUT 代表的是 replacement semantics,而 PATCH 是 UPSERT semantics。

对于一个已经存在的 item,PUT 做的是全量替换,而 PATCH 做的是部分替换。即 PUT 请求中提供的 item 属性都会替换进资源本身,如果有一个原有的属性在 PUT 中消失了,那么这个属性在服务端也会被删除;而 PATCH 不会做删除属性这种事情,它只会更新原有的属性。

对于 item 还未存在时,PUT / PATCH 做的都是 insert 语义。

表达错误

7.10.2 Error condition responses 一节描述了服务端如何将错误信息告诉客户端,值得借鉴。给一个例子:

{
  "error": {
    "code": "BadArgument",
    "message": "Previous passwords may not be reused",
    "target": "password",
    "innererror": {
      "code": "PasswordError",
      "innererror": {
        "code": "PasswordDoesNotMeetPolicy",
        "minLength": "6",
        "maxLength": "64",
        "characterTypes": ["lowerCase","upperCase","number","symbol"],
        "minDistinctCharacterTypes": "2",
        "innererror": {
          "code": "PasswordReuseNotAllowed"
        }
      }
    }
  }
}

Collection URL Patterns

9 Collections 给出了针对集合(collection)的一些 URL pattern,值得借鉴。一些例子:

  • Nested collections and properties: GET https://api.contoso.com/v1.0/people/123/addresses
  • Order by: GET https://api.contoso.com/v1.0/people?$orderBy=name

里面的 filter, order by,分页 等 URL pattern,使用了 OData 提倡的 URL 样式。

filter, sort 时,需要考虑与后端存储的配合。

分页

分页有两种模式,他们可以单独用也可以混合用:

  • server-driven: server 指定了一页的 item 数,并提供 nextLink 告诉客户端下一页的 URL
  • client-driven: client 通过请求参数指定一页的 item 数;这个参数超过 server 端配置时,使用 server 端配置

9.8.3 Additional considerations 提到一些分页需要考虑的点:

  • 排序稳定性:server 必须提供额外的排序条件来满足这一点
  • missing/repeated result:分页协议的设计要考虑这一点

Opaque URL

对于一些需要给出后续链接的情况,比如对一个大的集合做了分页后提供的 nextLink,往往使用 opaque URL 会合适:

{
  "value":[
    { "id": "Item 1","price": 99.95,"sizes": null},
    { … },
    { … },
    { "id": "Item 99","price": 59.99,"sizes": null}
  ],
  "@nextLink": "{opaqueUrl}"
}

这篇文章 描述了什么是 opaque URL。大致上是一种防止用户使用未文档的 URL pattern 的方法,使得开发者可以更容易地去更新 URL。

如果不用 opaque URL,可能会把细节透露给 client。对于一个没有公开文档的 URL,它可能有两种设计:

  • Hackable URL: /items?page=3
  • Opaque URL: /DC884298C

比如一开始,我的 nextLink 长得像这样:/items?page=2;后来我决定换成 cursor 式:/items?nextCursor=xxx。如果不用 opaque URL,有些用户可能会在代码中硬编码规则,导致你无法更换 URL pattern。

Delta queries

10 Delta queries 描述一套模式,使客户端可以监控某些 item 的变化。这一节我觉得写得不好。它在尝试用简单的协议(可能为了通用),来描述一件复杂的事情。最后可能并不是太实用。

Long running operations

13 Long running operations 描述了一套模式,用于处理 long running operation。这里的核心在于把 operation 也抽象成一种资源。

Unsupported requests

前文提到的 collection URL pattern 是一些通用的模式,但是有一些 item 可能不支持里面的部分操作(比如 sort, filter 等)。15 Unsupported requests 描述了一套协议,用于告诉客户端 server 不支持什么。

Naming guidelines

16 Naming guidelines 有一些有价值的规则供参考,如:

  • Services SHOULD avoid using articles such as 'a', 'the', 'of' unless needed to convey meaning.
    • e.g. names such as aUser, theAccount, countOfBooks SHOULD NOT be used, rather user, account, bookCount SHOULD be preferred.
  • When adding a type to a property name, services MUST add the type at the end, e.g. createdDateTime.
    • For properties requiring both date and time, services MUST use the suffix 'DateTime'.
    • For properties requiring only date, use suffix 'Date'

同时我见过一个说法,在描述数量时,把 num 提前,比如 numPerson, numBooks