API的定义取决于选择的IPC通讯方式,若是是消息机制(如 AMQP 或者 STOMP),API则由消息频道(channel)和消息类型;若是是使用HTTP机制,则是基于请求/响应(调用http的url),这里咱们先简述下RestfulAPI的定义。git
应该尽可能将API部署在专用域名之下,如:github
https://api.example.com
也能够放在主域名下:数据库
https://example.org/api/
放入到头信息的Accept中
制定版本并在版本之间平缓过渡对于设计和维护一套API是个巨大的挑战。因此,最好在设计之初就使用一些方法来预防可能会遇到的问题。
为了不API的变更致使用户使用中产生意外结果或调用失败,最好强制要求全部访问都须要指定版本号。请避免提供默认版本号,一旦提供,往后想要修改它会至关困难。
最适合放置版本号的位置URL中,或者是头信息(HTTP Headers)中在 Accept 段中使用自定义类型(content type)与其余元数据(metadata)一块儿提交。json
https://api.example.com/v1/ 或 Accept: application/vnd.heroku+json; version=3
为每个请求响应包含一个Request-Id字段,并使用UUID做为该值。经过在客户端、服务器或任何支持服务上记录该值,它能主咱们提供一种机制来跟踪、诊断和调试请求。api
在RESTful架构中,每一个网址表明一种资源(resource),因此网址中不能有动词,只能有名词,并且所用的名词每每与数据库的表格名对应。通常来讲,数据库中的表都是同种记录的”集合”(collection),因此API中的名词也应该使用复数。
举例来讲,有一个API提供动物园(zoo)的信息,还包括各类动物和雇员的信息,则它的路径应该设计成下面这样。数组
https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees
好的末尾不须要为资源指定特殊的行为,但在特殊状况下,为某些资源指定行为倒是必要的。为了描述清楚,在行为前加上一个标准的actions:服务器
/resources/:resource/actions/:action
如:restful
/runs/{run_id}/actions/stop
为了和域名命名规则保持一致,使用小写字母并用-分割路径名字,例如:架构
service-api.com/users service-api.com/app-setups
属性也使用小写字母,可是属性名要用下划线_分割,以便在Javascript中省略引号。 例如:app
service_class: "first"
在某些状况下,让用户提供ID去定位资源是不方便的。例如,一个用户想取得他在Heroku平台app信息,可是这个app的惟一标识是UUID。这种状况下,你应该支持接口经过名字和ID都能访问,例如:
$ curl https://service.com/apps/{app_id_or_name} $ curl https://service.com/apps/97addcf0-c182 $ curl https://service.com/apps/www-prod
不要只接受使用名字而放弃了使用id。
在一些有父路径/子路径嵌套关系的资源数据模块中,路径可能有很是深的嵌套关系,例如:
/orgs/{org_id}/apps/{app_id}/dynos/{dyno_id}
推荐在根(root)路径下指定资源来限制路径的嵌套深度。使用嵌套指定范围的资源。在上述例子中,dyno属于app,app属于org能够表示为:
/orgs/{org_id} /orgs/{org_id}/apps /apps/{app_id} /apps/{app_id}/dynos /dynos/{dyno_id}
对于资源的具体操做类型,由HTTP动词表示。
经常使用的HTTP动词有下面五个(括号里是对应的SQL命令):
GET(SELECT):从服务器取出资源(一项或多项)。 POST(CREATE):在服务器新建一个资源。 PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。 PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。 DELETE(DELETE):从服务器删除资源。
一些例子:
GET /zoos:列出全部动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的所有信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的全部动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
若是记录数量不少,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数:
?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page=2&per_page=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪一个属性排序,以及排序顺序。 ?animal_type_id=1:指定筛选条件
参数的设计容许存在冗余,即容许API路径和URL参数偶尔有重复。好比,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
服务器向用户返回的状态码和提示信息,常见的有如下一些(方括号中是该状态码对应的HTTP动词):
200 OK - [GET]:服务器成功返回用户请求的数据,该操做是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操做,该操做是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户获得受权(与401错误相对),可是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操做,该操做是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(好比用户请求JSON格式,可是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再获得的。 422 Unprocesable entity - [POST/PUT/PATCH] 当建立一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将没法判断发出的请求是否成功。
在默认状况给每个资源一个id属性。除非有更好的理由,不然请使用UUID。不要使用那种在服务器上或是资源中不是全局惟一的标识,尤为是自动增加的id。
生成小写的UUID格式 8-4-4-4-12,例如:
"id": "01234567-89ab-cdef-0123-456789abcdef"
为资源提供默认的建立时间 created_at 和更新时间 updated_at,例如:
{ ... "created_at": "2012-01-01T12:00:00Z", "updated_at": "2012-01-01T13:00:00Z", ... }
在接收和返回时都只使用UTC格式(ISO8601格式的数据)或者使用时间戳。,例如:
"finished_at": "2012-01-01T12:00:00Z"
或
"timestamp": "1472486035"
若是状态码是4xx,就应该向用户返回出错信息。通常来讲,返回的信息中将error做为键名,出错信息做为键值便可。
{ error: "Invalid API key" }
针对不一样操做,服务器向用户返回的结果应该符合如下规范。
GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档
目前为保证响应最小化,通常使用json字符串,而且请求中多余的空格会增长响应大小,并且如今不少的HTTP客户端都会本身输出可读格式("prettify")的JSON。因此最好保证响应JSON最小化,例如:
{"beta":false,"email":"alice@heroku.com","id":"01234567-89ab-cdef-0123-456789abcdef","last_login":"2012-01-01T12:00:00Z","created_at":"2012-01-01T12:00:00Z","updated_at":"2012-01-01T12:00:00Z"}
而不是这样:
{ "beta": false, "email": "alice@heroku.com", "id": "01234567-89ab-cdef-0123-456789abcdef", "last_login": "2012-01-01T12:00:00Z", "created_at": "2012-01-01T12:00:00Z", "updated_at": "2012-01-01T12:00:00Z" }
RESTful API最好作到Hypermedia,即返回结果中提供连接,连向其余API方法,使得用户不查文档,也知道下一步应该作什么。
好比,当用户向api.example.com的根目录发出请求,会获得这样一个文档。
{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会获得一个全部可用API的网址列表。
{ "current_user_url": "https://api.github.com/user", "authorizations_url": "https://api.github.com/authorizations", // ... }
从上面能够看到,若是想获取当前用户的信息,应该去访问api.github.com/user,而后就获得了下面结果。
{ "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3" }
上面代码表示,服务器给出了提示信息,以及文档的网址。
by 刘迎光@萤火虫工做室
OpenBI交流群:495266201
MicroService 微服务交流群:217722918
mail: liuyg#liuyingguang.cn
博主首页(==防止爬虫==):http://blog.liuyingguang.cn
OpenBI问答社区:http://www.openbi.tk