7

本文主要简述API设计的要点。整体会偏向HTTP的接口,部分同样适合非HTTP的接口。

API版本管理

多版本兼容(控制维护的版本数,不宜过多)

HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
api-version: 2

spring中直接@RequestMapping(headers="api-version=2")即可以自动分发

  • content-type accept

HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
Accept: application/vnd.haveibeenpwned.v2+json
  • content-type accept(版本单独字段)

HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
Accept: application/vnd.haveibeenpwned+json; version=2.0

如何下架旧版本(对过期版本的处理)

  • 对既有版本的访问进行监控

  • 对要下架的版本随机返回错误

API安全认证(两方认证vs三方认证)

两方认证,只涉及api服务提供方和使用方;

clipboard.png

三方认证,多了个用户,通常是采用OAuth2来进行授权认证。

clipboard.png

下面的这些仅仅涉及两方认证(双方认证的 api 通常的认证方式是使用一组 id 和密钥,用 id 来标记应用,用密钥来对请求做签名):

  • 开启https(web api的话)

  • Http Basic Authentication(一般内网使用配合https)
    在 Http header 中添加键值对 Authorization: Basic xxx (xxx 是 username:passowrd base64 值)。

  • 黑白名单(内网白,外网黑)

  • JWT (JSON Web Token)(适合移动端)
    JWT认证协议的格式为Header.Payload.Signature。比如xxxxx.yyyyy.zzzzz。签名内容是有Header+Payload+Secret通过HMAC SHA256算法加密而成。

clipboard.png
具体参考Introduction to JSON Web Tokens

API限流措施

HTTP/1.1 403 Forbidden
Date: Tue, 20 Aug 2013 14:50:41 GMT
Status: 403 Forbidden
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1377013266
{
"message": "API rate limit exceeded. \
See http://developer.github.com/v3/ \
#rate-limiting for details."
}

API的文档化

业界的两个方案,一个是Swagger使用Swagger快速打造REST API文档,一个是最新的spring restdocs

API的接口设计

  • 错误码处理
    建议充分利用http的错误码,然后再细分,不建议自己再重新自定义一套。

    • 请求成功

      • 200 OK : 请求执行成功并返回相应数据,如 GET 成功

      • 201 Created : 对象创建成功并返回相应资源数据,如 POST 成功;创建完成后响应头中应该携带头标 Location ,指向新建资源的地址

      • 202 Accepted : 接受请求,但无法立即完成创建行为,比如其中涉及到一个需要花费若干小时才能完成的任务。返回的实体中应该包含当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便客户端能够获取最新状态。

      • 204 No Content : 请求执行成功,不返回相应资源数据,如 PATCHDELETE 成功

    • 重定向
      重定向的新地址都需要在响应头 Location 中返回

      • 301 Moved Permanently : 被请求的资源已永久移动到新位置

      • 302 Found : 请求的资源现在临时从不同的 URI 响应请求

      • 303 See Other : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该使用 GET 方法进行请求

      • 307 Temporary Redirect : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该保持原有的请求方法进行请求

    • 条件请求

      • 304 Not Modified : 资源自从上次请求后没有再次发生变化,主要使用场景在于实现数据缓存

      • 409 Conflict : 请求操作和资源的当前状态存在冲突。主要使用场景在于实现并发控制

      • 412 Precondition Failed : 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。主要使用场景在于实现并发控制

    • 客户端错误

      • 400 Bad Request : 请求体包含语法错误

      • 401 Unauthorized : 需要验证用户身份,如果服务器就算是身份验证后也不允许客户访问资源,应该响应 403 Forbidden

      • 403 Forbidden : 服务器拒绝执行

      • 404 Not Found : 找不到目标资源

      • 405 Method Not Allowed : 不允许执行目标方法,响应中应该带有 Allow 头,内容为对该资源有效的 HTTP 方法

      • 406 Not Acceptable : 服务器不支持客户端请求的内容格式,但响应里会包含服务端能够给出的格式的数据,并在 Content-Type 中声明格式名称

      • 410 Gone : 被请求的资源已被删除,只有在确定了这种情况是永久性的时候才可以使用,否则建议使用 404 Not Found

      • 413 Payload Too Large : POST 或者 PUT 请求的消息实体过大

      • 415 Unsupported Media Type : 服务器不支持请求中提交的数据的格式

      • 422 Unprocessable Entity : 请求格式正确,但是由于含有语义错误,无法响应

      • 428 Precondition Required : 要求先决条件,如果想要请求能成功必须满足一些预设的条件

    • 服务端错误

      • 500 Internal Server Error : 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。

      • 501 Not Implemented : 服务器不支持当前请求所需要的某个功能。501405 的区别是:405 是表示服务端不允许客户端这么做,501 是表示客户端或许可以这么做,但服务端还没有实现这个功能

      • 502 Bad Gateway : 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。

      • 503 Service Unavailable : 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间(内容可以为数字,单位为秒;或者是一个 HTTP 协议指定的时间格式)。如果没有给出这个 Retry-After 信息,那么客户端应当以处理 500 响应的方式处理它。

    • 错误码带描述

503 Service Unavailable
{
"error_human": "The Database is currently unavailable.",
"error_code": "database_unavailable" (使用文字描述比起使用数字code会使开发者更容易懂)
}
  • 分页

GET /employees?left=true&fields=name,joinDate&limit=10&offset=0&sortby=name&order=asc
{
    "data": {
        "total": 150,
        "employees": [
            {
                "name": "tom",
                "joinDate": "2015-10-01"
            },
            {
                "name": "jack",
                "joinDate": "2015-10-02"
            }
        ]
    }
}

或者采用page的方式同时加入links提示

?page=1&pre_page=10
{
    "data": {
        "total": 150,
        "links": [
            {
                "rel": "next",
                "href": "?page=2&per_page=10"
            },
            {
                "rel": "last",
                "href": "?page=11&per_page=10"
            }
        ],
        "employees": [
            {
                "name": "tom",
                "joinDate": "2015-10-01"
            },
            {
                "name": "jack",
                "joinDate": "2015-10-02"
            }
        ]
    }
}

或者

?limit=10: Reduce the number of results returned to the Consumer (for Pagination)
?offset=10: Send sets of information to the Consumer (for Pagination)
?limit=25&offset=50
  • 排序

?sortby=name&order=asc: Sort the results based on the specified attribute (ORDER BY name ASC)
  • 查询字段选择

GET http://api.example.org/user/12?fields=id,name,email (SELECT id, name, email FROM user WHERE user.id = 12) 
  • 查询过滤

?q=fluffy+fur(搜索)
?animal_type_id=1: Filter records which match the following condition (WHERE animal_type_id = 1)
  • 其他

    • HATEOAS
      HATEOAS是Hypermedia as the Engine of Application State的缩写形式,中文意思为:超媒体应用状态引擎。它的核心思想是使用超媒体表达应用状态,与hypertext-driven思想是一致的。在此之前,我们大多数的程序业务控制在前台完成。例如:我们会在前台做注册流程,我们在前台判定下一步应该做什么,可以做什么。当使用HATEOAS时,这些状态流程控制都在应用程序的后台完成。我们使用超媒体来表达前台做完某一步骤之后可以做哪些? 这样一来,前台的任务就变得相当简单了,前台需要处理的是理解状态表述,数据收集和结果显示。

    • url组成

      • 网络协议(HTTP, HTTPS)

      • 服务器地址

      • 版本

      • 接口名称(用-代替_

      • ?参数列表(之后返回接口instagram是用_代替驼峰的

    • url命名限制

      • 不使用大写字母

      • 使用中线-代替下划线_,比如instagram的一个接口,https://api.instagram.com/v1/users/{user-id}/followed-by?access_token=ACCESS-TOKEN

      • 参数列表应该被encode过

    • 聚合资源必须通过父级资源操作
      示例: Profile是User的聚合资源,User有一个唯一且私有的Profile资源,只能通过User操作Profile。/users/123456/profiles

    • 组合资源要避免资源路径嵌套
      示例: 一个系统里面包含多个 applications,一个 application 又包含多个 users。那获取 user 资源的路径应该是怎样的?

 GET http://~/$version/systems/:systemId/applications/:applicationId/users/:userId

改为

GET http://~/$version/systems/:systemId
GET http://~/$version/applications/:applicationId
GET http://~/$version/users/:userId/

参考


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

1 篇内容引用
0 条评论