restful api 设计

API应该对程序员友好,并且在浏览器地址栏容易输入。

API应该简单,直观,容易使用的同时优雅。

API应该具有足够的灵活性来支持上层ui。

一旦定义好了要暴露的资源,你可以定义资源上允许的操作,以及这些操作和你的API的对应关系:

GET/tickets#获取ticket列表

GET/tickets/12#查看某个具体的ticket

POST/tickets#新建一个ticket

PUT/tickets/12#更新ticket12.

DELETE/tickets/12#删除ticekt12

可以看出使用REST的好处在于可以充分利用http的强大实现对资源的CURD功能。而这里你只需要一个endpoint:/tickets,再没有其他什么命名规则和url规则了

如何处理关联?关于如何处理资源之间的管理REST原则也有相关的描述:

GET/tickets/12/messages-Retrieveslistofmessagesforticket#12

GET/tickets/12/messages/5-Retrievesmessage#5forticket#12

POST/tickets/12/messages-Createsanewmessageinticket#12

PUT/tickets/12/messages/5-Updatesmessage#5forticket#12

PATCH/tickets/12/messages/5-Partiallyupdatesmessage#5forticket#12

DELETE/tickets/12/messages/5-Deletesmessage#5forticket#12

其中,如果这种关联和资源独立,那么我们可以在资源的输出表示中保存相应资源的endpoint。然后API的使用者就可以通过点击链接找到相关的资源。如果关联和资源联系紧密。资源的输出表示就应该直接保存相应资源信息。(例如这里如果message资源是独立存在的,那么上面GET/tickets/12/messages就会返回相应message的链接;相反的如果message不独立存在,他和ticket依附存在,则上面的API调用返回直接返回message信息)

不符合CURD的操作

对这个令人困惑的问题,下面是一些解决方法:

重构你的行为action。当你的行为不需要参数的时候,你可以把active对应到activated这个资源,(更新使用patch).

以子资源对待。例如:github上,对一个gists加星操作:PUT/gists/:id/star并且取消星操作:DELETE/gists/:id/star.

有时候action实在没有难以和某个资源对应上例如search。那就这么办吧。我认为API的使用者对于/search这种url也不会有太大意见的(毕竟他很容易理解)。只要注意在文档中写清楚就可以了。

永远使用SSL

毫无例外,永远都要使用SSL。你的应用不知道要被谁,以及什么情况访问。有些是安全的,有些不是。使用SSL可以减少鉴权的成本:你只需要一个简单的令牌(token)就可以鉴权了,而不是每次让用户对每次请求签名。

值得注意的是:不要让非SSL的url访问重定向到SSL的url。

文档

最好能提供文档在线管理

结果过滤,排序,搜索:

url最好越简短越好,和结果过滤,排序,搜索相关的功能都应该通过参数实现(并且也很容易实现)。

过滤:为所有提供过滤功能的接口提供统一的参数。例如:你想限制get/tickets的返回结果:只返回那些open状态的ticket–get/tickektsstate=open这里的state就是过滤参数。

排序:和过滤一样,一个好的排序参数应该能够描述排序规则,而不业务相关。复杂的排序规则应该通过组合实现:

GET/ticketssort=-priority-Retrievesalistofticketsindescendingorderofpriority

GET/ticketssort=-priority,created_at-Retrievesalistofticketsindescendingorderofpriority.Withinaspecificpriority,olderticketsareorderedfirst

这里第二条查询中,排序规则有多个rule以逗号间隔组合而成。

搜索:有些时候简单的排序是不够的。我们可以使用搜索技术(ElasticSearch和Lucene)来实现(依旧可以作为url的参数)。

GET/ticketsq=return&state=open&sort=-priority,created_at-Retrievethehighestpriorityopenticketsmentioningtheword‘return’

对于经常使用的搜索查询,我们可以为他们设立别名,这样会让API更加优雅。例如:

get/ticketsq=recently_closed->get/tickets/recently_closed.

限制API返回值的域

有时候API使用者不需要所有的结果,在进行横向限制的时候(例如值返回API结果的前十项)还应该可以进行纵向限制。并且这个功能能有效的提高网络带宽使用率和速度。可以使用fields查询参数来限制返回的域例如:

GET/ticketsfields=id,subject,customer_name,updated_at&state=open&sort=-updated_at

更新和创建操作应该返回资源

PUT、POST、PATCH操作在对资源进行操作的时候常常有一些副作用:例如created_at,updated_at时间戳。为了防止用户多次的API调用(为了进行此次的更新操作),我们应该会返回更新的资源(updatedrepresentation.)例如:在POST操作以后,返回201created状态码,并且包含一个指向新资源的url作为返回头

鉴权Authentication

restfulAPI是无状态的也就是说用户请求的鉴权和cookie以及session无关,每一次请求都应该包含鉴权证明。

通过使用ssl我们可以不用每次都提供用户名和密码:我们可以给用户返回一个随机产生的token。这样可以极大的方便使用浏览器访问API的用户。这种方法适用于用户可以首先通过一次用户名-密码的验证并得到token,并且可以拷贝返回的token到以后的请求中。如果不方便,可以使用OAuth2来进行token的安全传输。

支持jsonp的API需要额外的鉴权方法,因为jsonp请求无法发送普通的credential。这种情况下可以在查询url中添加参数:access_token。注意使用url参数的问题是:目前大部分的网络服务器都会讲query参数保存到服务器日志中,这可能会成为大的安全风险。

注意上面说到的只是三种传输token的方法,实际传输的token可能是一样的。

缓存

HTTP提供了自带的缓存框架。你需要做的是在返回的时候加入一些返回头信息,在接受输入的时候加入输入验证。基本两种方法:

ETag:当生成请求的时候,在HTTP头里面加入ETag,其中包含请求的校验和和哈希值,这个值和在输入变化的时候也应该变化。如果输入的HTTP请求包含IF-NONE-MATCH头以及一个ETag值,那么API应该返回304notmodified状态码,而不是常规的输出结果。

Last-Modified:和etag一样,只是多了一个时间戳。返回头里的Last-Modified:包含了RFC1123时间戳,它和IF-MODIFIED-SINCE一致。HTTP规范里面有三种date格式,服务器应该都能处理。

出错处理

就像html错误页面能够显示错误信息一样,API也应该能返回可读的错误信息–它应该和一般的资源格式一致。API应该始终返回相应的状态码,以反映服务器或者请求的状态。API的错误码可以分为两部分,400系列和500系列,400系列表明客户端错误:如错误的请求格式等。500系列表示服务器错误。API应该至少将所有的400系列的错误以json形式返回。如果可能500系列的错误也应该如此。json格式的错误应该包含以下信息:一个有用的错误信息,一个唯一的错误码,以及任何可能的详细错误描述。如下:

{

"code":1234,

"message":"Somethingbadhappened:-(",

"description":"Moredetailsabouttheerrorhere"

}

对PUT,POST,PATCH的输入的校验也应该返回相应的错误信息,例如:

{

"code":1024,

"message":"ValidationFailed",

"errors":[

{

"code":5432,

"field":"first_name",

"message":"Firstnamecannothavefancycharacters"

},

{

"code":5622,

"field":"password",

"message":"Passwordcannotbeblank"

}

]

}

HTTP状态码

200ok-成功返回状态,对应,GET,PUT,PATCH,DELETE.

201created-成功创建。

304notmodified-HTTP缓存有效。

400badrequest-请求格式错误。

401unauthorized-未授权。

403forbidden-鉴权成功,但是该用户没有权限。

404notfound-请求的资源不存在

405methodnotallowed-该http方法不被允许。

410gone-这个url对应的资源现在不可用。

415unsupportedmediatype-请求类型错误。

422unprocessableentity-校验错误时用。

429toomanyrequest-请求过多。

原文链接:VinaySahni,编译:感谢@bruce-accumulate的热心翻译

译文链接:http://blog.jobbole.com/41233/

相关推荐