Base URL
那么应该选择哪URL呢?
http(s)://api.foo.com VS http(s)://www.foo.com/dev/service/api/rest
选择的原则是:简单,方便。所以可以得话还是选第一种。Rest Client 和 Browser 访问 http(s)://api.foo.com, 各应该返回什么结果?
原则是保持返回结果一致:这样会方便开发者debug 和了解 API,因为这样可以让开发者无论使用什么工具都可以方便的得到相同的反馈。版本号 两种方法可选:
URL方式
http(s)://api.example.com/v1
或者
Media-Type
application/json+foo;application&v=1
该选择哪种方式呢?
URL方式看起来简洁易懂,问题是如果版本号变更很频繁,对于调用API的开发者而言将会很痛苦。所以但采用这种方式时,没有大的变更不要频繁的变更版本号,尽量保持版本号为整型(int), 不要使用像 2.3、1.2.2 之类的小版本号。
Media-Type 可以让开发者在http请求头Acccept中指定的想要访问API的版本号:
Accept: application/json+foo;application&v=1
Server 可以通过检查这个请求头再确定版本号。这种方式的缺点是,要花很多时间去设计,实现和支持 Media-Type。(这中也是最符合http标准的方式)
总结:这两种方式都可以,但是开发者倾向于URL方式,因为 。。。 简单。
camelCase?
JSON 是JavaScript object notation 的简称,也就是说 JSON 是JavaScript 原生的格式。 JavaScript的规范中要求使用驼峰式(camelCase), 那我们最好也要遵守这个规范, 保持命名的一致性。
JavaScript 中的方法是遵守规范的:
myArray.forEach
而不是 myArray.for_each
所以我们最好写成 account.givenName
,而不是这样account.given_name
怎么表示 Date/Time/Timestamp
这个已经有标准,那就是 ISO 8601
例子:
{
…,
“createdTimeStamp”:”2012-07-10T18:02:24.343z”
}
在JSON中使用HREF
- 实现分布式超媒体十分重要(我理解超媒体的意思跟超文本差不多,可以链接到其他Media Type)
- 每一个可访问的资源都应该有一个独有URL.
- 用HREF取代ID表示唯一资源方式
{
“href”:https://api.example.com/v1/accounts/1234
}
在返回的JSON中加入href属性来表示说访问资源的位置,当然可以添加相关资源的href,这样就实现的资源之间的链接,也就是上面提到的超媒体(HyperMeida)。
响应体(Response Body)应该返回什么?
对Get来说这很明显,返回请求内容即可。
对于POST来说,是返回最新更新或者创建的资源的内容吗?
如果用POST更新的某个资源,而客户端也需要使用这个资源来更新到最新数据或更新缓存,那这种合理的情况下应该返回更新的资源。
但是也会有返回资源不合理的情况,如:上传文件,客户端就没有必要再获得从服务器返回的上传文件。另一种情况是有访问数据量的限制,每次都返回肯定会消耗数据量配额。
解决方式:可以在请求的链接后跟上类似(?_body=false),需要返回响应体时指定__body=true, 不需要时指定_body=false
响应体内容格式
- Accept Header :
通过请求头可以指定返回的数据类型,格式为:
GET /applications/a1b2c3
Accept: application/json, text/plain //从左到右,优先级依次降低
这是传统的方式,还有另外一种方式,对用户很友好 资源后缀:
/applications/a1b2c3.json /applications/a1b2c3.csv
可以用这种方来覆盖上边传统的方式,也就是同时支持上面提到的两种方式,如果用户在url通过 .json 或 .csv 指定了格式,那就采用url指定的方式
资源引用
这个对Rest API 的拓展性很重要- 单个实体
GET /accounts/x7y8z9
200 ok
{
"href": "https://api.example.com/v1/accounts/x7y8z9",
"givenName": "Tony",
"surname": "Stark",
...,
"directory": {
"href":"https://api.stormpath/v1/directories/g4b5i6'
}
}
这样当请求某个account是,会得到与这个account相关的 directory的url, 通过这个url,就可以访问这个directory
那么怎么,一次请求返回 account 和directory的信息,而不是只返回directory的url,可以在url后边加上?expand=true,如下:
GET /accounts/x7y8z9?expand=true
200 ok
{
"href": "https://api.example.com/v1/accounts/x7y8z9",
"givenName": "Tony",
"surname": "Stark",
...,
"directory": {
"href":"https://api.stormpath/v1/directories/g4b5i6' ,
"name":"Avengera",
"creationDate":...
}
}
如果只想要一部分的结果呢?可以通过添加fields 字段来指定想要返回的字段,如下
`
GET /accounts/x7y8z97? fields=givenName,surname,directory(name)
· 集合
怎么做分页呢?可以添加offset和limit作为参数,如下:
/applications?offset=50&limit=25
这种方式的缺点在于还需要用户来指定查询相关的关键字想offset和limit,下面这种方式 是通过资源应用的思想,如下
GET /accounts/x7y8z9/groups
·
200 OK
{
"meta":{}
"offset":0,
"limit": 25,
"first": {"meta":".../accounts/x7y8z9/groups?offset=0"},
"previouse":null,
"last": {"meta":".../accounts/x7y8z9/groups?offset=25"},
"items": {
{
"meta":{"href":"..."}
},
{
"meta":{"href":"..."}
}
}
}
这种方式通过资源链接的方式,可以很方便的获取想过相关的资源的访问url,想第一个资源,最后一个资源等等。怎么处理many to many的情况?
- 一个group可以有多个account
- 每个account可能在多个group里
- account和group的mapping(GroupMembership)也看作是资源
通过访问 GroupMembership 来获得account和group的信息
GET /groupMemberships/2143214
200 OK
{
"href": "/groupMemberships/2143214",
"account": {"href":""},
"group": {"href":""}
}
通过account获得其他信息,如下
GET /accounts/x7y8z9
200 OK
{
"href": ".../accounts/x7y8z9",
"givenName":"Tony",
"gorups": {"href":".../accounts/x7y8z9/groups"},
"groupMemberships": {"href":".../groupMemberships/accountId=x7y8z9"}
}
这里既返回了groups的信息,又返回了groupMemberships的信息,这样可以给用户更多的选择。
- 单个实体