RESTful API 设计最佳实践(7)
RESTful API 设计最佳实践(7)本篇博客将侧重介绍在RESTful API设计中,消息头HEADER和消息体body相关的东西。URL只是RESTful API设计的主要一部分,要实现REST的统一接口,HTTP协议中的其他部分也不可或缺。关于统一接口,可查看我之前的博客。 一、返回新建资源的URL使用POST新建的资源时,如果创建成功,则返回201状态码,,应该在返回的消息头HEADER的 location 字段中,加上指向新建资源的URL,这是HTTP规范只一(参见rfc2616)。根据实际需要,可以在body中带上一些必要的信息。 也就是说,在RESTful API中,很多情况下,跟状态码+一些标准的header字段信息,就能够很快获知请求处理结果,而不一定非要从body中费力解析出来。但是往往我们会忽略掉这一点,而将所有处理结果信息全部塞到body中,如: :body {:result :success :id "新资源ID"}
这种方式其实还是RPC设计思维,并且单纯返回一个新资源的ID号也是不正确的(但估计绝大多数人的实现习惯中,都是这样做的)。因为在REST中,资源是用URL定义的,一个资源ID并不能标识资源在服务其中所处的位置。客户端通过post请求新建资源时,它并不知道服务器具体把它新建到哪里了,所以服务器应当返回一个具体的URL给客户端。 二、XML vs JSON vs EDN1. 格式的对比和选择下图为谷歌探索中查到的XMLAPI 、JSON API以及EDN使用量的数据(搜索不到EDN数据,因此用clojure代替——用clojure开发的,大多都用edn格式吧,不是很精确) 在HTTP请求的头部,可以通过 2. 几个实际问题
网上有资料建议放在URL中,比如在URL的后面加一个”.json”的字符串表明想要的回应消息格式,这样在浏览器中就能够很方便地调用了。这个理由是非常糟糕的。
建议:不同的语言使用其惯用的命名风格,你可以写一个公共的库进行格式转化。比如js/java/C#等语言使用驼峰命名法,cojure/clojurescript则使用:key以及user-id形式,python/ruby则用下划线的形式命名。 三、分页通常,我们可能会很习惯地由客户端记录当前所处页,再组装上一页/下一页链接来进行翻页,但在REST服务中,更好的方式是由服务端组装好上一页/下一页的分页链接,随着本页信息一起回送给客户端。因为在REST服务中,是以超媒体为驱动的,因而在返回的资源信息中带有关联资源的url(超链接)是很自然的事情。如:github的处理方式如下: Link: https://api.github.com/user/repos?page=3&per_page=100; rel=”next”,https://api.github.com/user/repos?page=50&per_page=100; rel=”last”
具体可参考github和facebook分页的API。 四、API调用频率限制为了避免API被频繁调用(如恶意刷),通常会对每个客户端对同一个API的访问频率进行限制,在HTTP规范中,用状态码429表示请求过多。Twitter对此的处理方式是在响应头部写入限制信息,如: *X-Rate-Limit-Limit : 当前时间段内允许访问的次数。
*X-Rate-Limit-Remaining : 当前时间段内剩余访问次数。
*X-Rate-Limit-Reset : 当前时间段剩余时间
对于超过限制的访问,将返回429状态码,并在body中给出相应提示: {“errors” : [{“code”: 88,“message”: “Rate limit exceeded”}]}
Sun,06 Nov 1994 08:49:37 GMT ; RFC 822,updated by RFC 1123
Sunday,06-Nov-94 08:49:37 GMT ; RFC 850,obsoleted by RFC 1036
Sun Nov 6 08:49:37 1994 ; ANSI C’s asctime() format
所以在header中表达时间时,理论上应该遵循这几种格式。 个人觉得还是看场景需要,timestamp包含的信息更多,比如有时区等信息。然而在上面要表达还剩多少秒或毫秒的场景中,直接用还剩多少秒或毫秒的形式,可能会更加直观、实用,因为API使用者只关心还要过多久才能调用。 五、失败/异常消息返回失败/异常消息应该是可用、格式统一且易于被使用的,它也应该和系统其它资源一样,被当做一种资源返回,有它自己的属性,所以对于异常/失败这种资源,也需好好定义。 API调用成功或失败,都应该返回具有意义的状态码,而非全部返回200,然后在body中标记是否成功。错误码通常分4XX和5XX两类,下层分很多小类,我们必须仔细甄别当前异常属于哪一类。错误详细信息则可以在body中定义。 错误这一“资源”的body定义,简单的应该包括: {:code 1123
:msg “错误描述信息”
:desc “更多细节信息”}
复杂的异常信息定义可能包含更多的属性描述,比如校验客户端来的数据是否合法: {:code 1024
:msg “数据输入错误”
:errors [{:code 5432
:field :first-name
:msg “first name wrong!”}
{:code 5431
:field :password
:msg “password wrong!”}]}
这样不仅保持了整个REST服务基于资源进行抽象的一致性,也方便开发者调查异常原因,也便于灵活向用户展现错误信息。 而在我们的实际开发过程中,经常会在异常(提示)消息这些方面处理的比较随便,比如:
六、其他
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |