欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

RESTful API 设计指南

网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。

因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。我以前写过一篇《理解RESTful架构》,探讨如何理解这个概念。

今天,我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API。我的主要参考了两篇文章(12)。

RESTful API

一、协议

API与用户的通信协议,总是使用HTTPs协议

二、域名

应该尽量将API部署在专用域名之下。


https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。


https://example.org/api/

三、版本(Versioning)

应该将API的版本号放入URL。


https://api.example.com/v1/

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

四、路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址。

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

五、HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

  • 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:删除某个指定动物园的指定动物

六、过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。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 的含义是相同的。

七、状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里

八、错误处理(Error handling)

如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。


{
    error: "Invalid API key"
}

九、返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档

十、Hypermedia API

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"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

十一、其他

(1)API的身份认证应该使用OAuth 2.0框架。

(2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

(完)

珠峰培训

stuQ

留言(117条)

最后一条: "服务器返回的数据格式,应该使用JSON,不应使用XML" 存疑,返回某种自描述类的文本类数据即可,貌似没有规定不应使用XML;这与SOAP的rpc风格没有关联,两码事。

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

API还必须易于测试,能让用户在页面测试API,看到发送和接收的结果。

@fallingwine:

谢谢指出,改了一下。某些场合,确实不得不使用XML。

引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

這個整理得很好,讚呀。


我想問
如果 發生錯誤時,
只是拋出Status Code 真的好嗎?
如果想回應 錯誤的原因, 例如: 此日期不正確 ,之類

不好意思,忘記了可以更改Response Body.
{"status":"XXX","message":"XXX"}

有关搜索的 API 设计没有提到,是 search?q=?吗?

“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?

?sortby=name&order=asc
上面这种方式比较直观,如果用urlrewriter方式也不错,/sortby/name/order/asc,这样似乎又不符合一个url一个资源的规范

POST 一个对象时,对象的内容是怎么放到POST参数里面呢?

是把对象转成一个json,放在一个参数里面。
还是把对象的每个字段当初一个参数?

引用Yhzhtk的发言:

POST 一个对象时,对象的内容是怎么放到POST参数里面呢?

是把对象转成一个json,放在一个参数里面。
还是把对象的每个字段当初一个参数?

转成json放到request body里面。

目前公司也从WCF 向API 方面转移,定义与LZ雷同...

我一般是给 web app使用,但是浏览器对 PUT 这类支持不好,这个操作放到 URL 里合适吗?怎么处理比较好呢?

引用廖雪峰的发言:

API还必须易于测试,能让用户在页面测试API,看到发送和接收的结果。

开发人员一般都是chrome 或 firefox加插件调试

现在移动开发都是用api去调用数据,web也可以一起共用,节约开发成本。

引用Fengz的发言:

我一般是给 web app使用,但是浏览器对 PUT 这类支持不好,这个操作放到 URL 里合适吗?怎么处理比较好呢?

 

对PUT这类的操作也一直是和你有着同样的困惑。

其实这只是一个推荐规范, 为了解决一些浏览器的HTTP方法的兼容问题, 还有GET URL超长的问题, 感觉可以变通为, 把HTTP方法名称也放入URI中, 如:

/GET/zoos
/PUT/zoos/ID
/DELETE/zoos/ID

过长的参数可以自动使用POST方法来发送

用POST,《RESTful Web Services Cookbook》上有说过。

对于PUT和POST的用法,这篇文章和《RESTful Web Services Cookbook》上推荐的做法不一致。
Cookbook上的做法是如果用户可以决定新创建资源的URL,那么就用PUT,否则用POST。
以及,如果不仅仅是单纯的创建一个资源,类似复杂的工厂方法(会连带创建相关的其他复杂类属性),也用POST。

引用j.w的发言:


开发人员一般都是chrome 或 firefox加插件调试

推荐Fiddler

https://speakerdeck.com/akuzemchak/simple-api-development-with-laravel
这里有一个讲 rest api 的ppt,时代不错,有兴趣的同学可以看一下

restful 略蛋疼,跨域的更新操作怎么破?

引用andrewyang的发言:

“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?

同问。。。

引用fromoon的发言:

 

同问。。。

“理解RESTful架构”的评论里也有关于“版本”讨论,这也许和个人观点有关,我是觉得是可以的。有人在http://stackoverflow.com/questions/972226/how-to-version-rest-uris讨论哪种加版本号的方式更好。

你好 谢谢您的文章 我在我们系统中也使用这种架构 想问一下您 如果返回的数据量比较大的结构化数据 使用哪种方式速度上有优势

引用allenwest的发言:

如果批量獲取指定ID的一堆Zoo,按理應該用GET,但是由於ID列表可能會很長,這樣會導致url過長;如果放在HTTP Content裡面,但通常只有POST這些才支持,這樣又不符合HTTP動詞語義。請問如何破解?

若你使用的是 PHP,可以透過 http://www.hashids.org/php/ 來解決

引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

获取一堆的Zoo,你会经常发一堆的ID去查找吗?
我觉得获取列表往往只是发几个过滤条件,或者通过不同的endpoint,就足够了

版本应该在HTTP头中把.我一直认为Rest思想,其实就是HTTP协议的设计初衷.既然HTTP协议本身设计了版本,就应该走协议本身.而不是在协议上扩展.

你还记得你2007写的绕过gfw吗?

请教你:使用p2p技术能否把gfw打成一个千疮百孔的筛子,让其变成一个没有用的马其诺防线?

背景:
越来越受不了被人禁言和国内媒介的噤若寒蝉,特别是最近的事件,假期结束所有新闻平静沉默的吓人。

想法:
12年刚知道 BitCoin时被它的去中心技术所叹服。于是在想如果媒介或沟通也能去中心化,或者每个人都是一个中心,那么“擒贼先擒王”的办法再也无法控制每个单个个体。

方案:
A:去中心沟通工具
(1):做一个沟通工具,之间只能通过p2p链接。
(2):数据加密、源码开放。以BitCoin的方式认证源码。任何人都可以使用任何语言编写工具,但只能被认证通过的才可以进行沟通。防止间谍工具(代码)。
(3):p2p自我发现(嗅探)--每个终端开启后,可以自动嗅探到最近的其它终端,最终达到全网互联。
另:也可以使用初始其它p2p链接。即:一个p2p最终可以初始保存多个(如:1000+)其它p2p地址(如:至少有一个最近的,一个外省的,一个国内的,一个国外的)。相当于多服务器(多中心)。
B:去中心“服务器”
媒介(如:网易)只要服务器在国内或者公司在国内就不可能不受制约(断域名、断Ip、断人)。如果有一个服务器是存在全网(并不位于某一具体机器),那么如果在此服务器上部署的服务是不是就不受任何人控制。
(1):根据现在流行的大数据原理(所有结点构成整个分布式服务器,每个结点都可以充当中央结点)组建全网结点。
(2):每个具体用户贡献一部分cpu和内存、硬盘充当结点。
C:去中心“搜索引擎”
(1):同上,也是去中心“服务器”实现的基础。

引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

url的长度在http协议中中没有限制,只是在浏览器中有限制

引用AriesDevil的发言:

有关搜索的 API 设计没有提到,是 search?q=?吗?

同有点疑惑,如果按博主说的api中不能出现动词,那就不能用search了?

《理解RESTful架构》中有一段:另一个设计误区,就是在URI中加入版本号。而本文第3条又说:应该将API的版本号放入URL。两者相互冲突了。我觉得版本号不让人URL更合适一些。

.Net的WebAPI 还不能http跨域调用. AJAX使用JSONP访问是只有GET请求. 怎么扩展?

推荐RESTful API调试插件工具
Chrome:Postman/Advanced Rest Client
Firefox:RESTClient

感觉还是不要在URL里加入版本号的比较好。个人浅见。

引用WKPlus的发言:

 

同有点疑惑,如果按博主说的api中不能出现动词,那就不能用search了?

在参考了github的AIP后,对于搜索可以这样,你要搜索什么,如users, animals等,你可以在对应的后面加上一个搜索参数q={query},最后的组合是:
/users?q={query}这样的资源路径。

uri 中还是带有版本号好。

引用j.w的发言:


开发人员一般都是chrome 或 firefox加插件调试

配合curl很顺畅的。

应该将API的版本号放入URL。
正中误区
http://www.ruanyifeng.com/blog/2011/09/restful.html
另一个设计误区,就是在URI中加入版本号:

新的设计趋势?

引用fallingwine的发言:

最后一条: "服务器返回的数据格式,应该使用JSON,不应使用XML" 存疑,返回某种自描述类的文本类数据即可,貌似没有规定不应使用XML;这与SOAP的rpc风格没有关联,两码事。

基于JSON的优点,网络服务设计就是应该使用JSON而非陈旧的XML作为返回的数据格式~
这点没错,是时候抛弃XML了~

请问大师,如果顾客在控制台需要 “ 一次 ” 更改3个产品的数量,在后台数据库要如何处理事务回滚(transaction rollback )? 不知道是不是在对的地方问对的问题。。。

引用Mark的发言:

请问大师,如果顾客在控制台需要 “ 一次 ” 更改3个产品的数量,在后台数据库要如何处理事务回滚(transaction rollback )? 不知道是不是在对的地方问对的问题。。。

同问

openstack作为比较火的开源项目,里面有很多Restful API的设计,我觉得其应该经受得住考验,任何问题都可以参考它!

引用andrewyang的发言:

“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?

我也是看了上一个,一直记得不加版本号这个说法。

六、过滤信息(Filtering)
有时我们会有比较复杂的过滤比如 : >= , between , || , (color=red && size=s) 
这些要怎样用uri来表示呢?

引用nebula的发言:

 

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

get请求,url是有长度限制的

现在正在做一个基于RESTful API 架构的接口。但是遇到了一些令自己很困扰的问题:
假如每一个设备(devices)是拥有多张设备图片(images)的,那么按照RESTful,我们的URI 设计如下:

现在的设计:
GET /devices/ID/images:获取某个设备的所有图片
GET /devices/ID/images/ID:获取某个设备的某张图片(我们不提供改方法,有人认为没有意义的)
POST /devices/ID/images:给指定设备添加一张图片
PUT /Images/ID:修改某张图片(有疑问)
DELETE /images/ID : 删除某张图片(有疑问)

我觉得合理的设计是:
PUT /devices/ID/Images/ID:修改某个设备的某张图片
DELETE /devices/ID/images/ID : 删除某个设备的某张图片


看到,对于修改和删除某张图片,我是有疑问的,现在的删除是直接通过图片ID进行删除(不管这张图片属于哪一个设备的)。其实真的符合RESTful标准吗? 他们这样设计的理由是:由于图片本身就有自己的图片ID,为什么直接通过ID来删除呢,还非得指定某个设备的图片,然后再删除。

RESTful 强调资源的唯一性,images/ID其实就是唯一的图片资源,而/devices/ID/images/ID也是设备的唯一图片,那么,我都不知道应该采用哪一个URI的设计。

看到很多人提到关于版本号的问题,其实在“理解RESTful架构”一文两个误区中,提到的是资源不要使用URI来区分版本,但是这篇文章中所说的不同版本是指API 的版本,两个其实是不一样的。

个人认为版本放在headers 里更合适。放在url 里 确实有点膈应~
另外如果能讲讲 api 的设计过程。
实践出真知吗。期待 实战 api 设计

引用nebula的发言:

 

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

不美观?

注册、登录、登出这种api该怎么设计?
endpoint怎么定义,还有请求方法是用什么方法?

看来对REST API 还不够了解。
心是火热的,但路还很漫长,可惜项目不等人。
各类问题都很现实,趋于完善还需要时间。

引用nebula的发言:

 

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

浏览器url长度取决于 浏览器/服务器中较小的那一个.
这个问题我也是遇到过.在一些复杂查询(GET)时候,常常需要向后台传入多个查询参数,我自己大多数都是走post...

第三点介绍的:应该将API的版本号放入URL。但是在另一篇文章里http://www.ruanyifeng.com/blog/2011/09/restful.html介绍:另一个设计误区,就是在URI中加入版本号:

如果不用RESTFUL API 设计, 现有的HTTPs API 设计, 利用好HTTP协议, 设计上应该 不差到哪儿吧。

有哪位大侠能举例说明一下RESTFUL API 比现有的设计 有站得住脚的优势?

你上一篇博文有说“另一个设计误区,就是在URI中加入版本号”
博文地址:http://www.ruanyifeng.com/blog/2011/09/restful.html
和你当前这篇博文第三条有矛盾

引用kwk1024的发言:

 

url的长度在http协议中中没有限制,只是在浏览器中有限制

web服务器是有限制的啊!

引用低调学习的发言:

你上一篇博文有说“另一个设计误区,就是在URI中加入版本号”
博文地址:http://www.ruanyifeng.com/blog/2011/09/restful.html
和你当前这篇博文第三条有矛盾

楼主如果看到留言,希望能够分享一下你的看法,或者是业界流行的趋势?

你好,第一次留言,请问我如何使用 DELETE 请求传过去一个主键集合进行批量删除呢?

引用shadyxu的发言:

转成json放到request body里面。

那么在api接口处该如何接收

@华仔_SuHua:

我认为设备图片(images)是一种资源,设备(devices)只是图片的不同形式的条件而已,所以应该设计为 /images/ID?devices={id}

应该是翻译的吧,怎么好像成原创了。。http://www.tuicool.com/articles/goto?id=IRfYna

RESTful 现在是各显神通啊.
期待一个行业大佬给出最佳实践,指导我等小民.

引用某人路过的发言:

应该是翻译的吧,怎么好像成原创了。。http://www.tuicool.com/articles/goto?id=IRfYna

看上去是致敬(贼笑

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园

那么,请问,新建一个动物园的PAGE页面的URL应该怎样定义,即GET到新建动物园的页面,不可能是在/zoos这个所有动物园页面里面进行新建吧?

引用haofly的发言:

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园

那么,请问,新建一个动物园的PAGE页面的URL应该怎样定义,即GET到新建动物园的页面,不可能是在/zoos这个所有动物园页面里面进行新建吧?

页面URI:GET /zoos/creation
提交URI:POST /zoos

文章说了,动词的名词化表示一种服务,也是一种资源。资源不一定是存储在DB里的,静态资源类如image,大家很容易理解,但是创建页面所对应的html也是资源。资源的含义,对于一个系统来说,不仅限DB。本人拙见。

引用童奇的发言:

注册、登录、登出这种api该怎么设计?
endpoint怎么定义,还有请求方法是用什么方法?

同样是这个问题

引用华仔_SuHua的发言:

现在正在做一个基于RESTful API 架构的接口。但是遇到了一些令自己很困扰的问题:
假如每一个设备(devices)是拥有多张设备图片(images)的,那么按照RESTful,我们的URI 设计如下:

现在的设计:
GET /devices/ID/images:获取某个设备的所有图片
GET /devices/ID/images/ID:获取某个设备的某张图片(我们不提供改方法,有人认为没有意义的)
POST /devices/ID/images:给指定设备添加一张图片
PUT /Images/ID:修改某张图片(有疑问)
DELETE /images/ID : 删除某张图片(有疑问)

我觉得合理的设计是:
PUT /devices/ID/Images/ID:修改某个设备的某张图片
DELETE /devices/ID/images/ID : 删除某个设备的某张图片


看到,对于修改和删除某张图片,我是有疑问的,现在的删除是直接通过图片ID进行删除(不管这张图片属于哪一个设备的)。其实真的符合RESTful标准吗? 他们这样设计的理由是:由于图片本身就有自己的图片ID,为什么直接通过ID来删除呢,还非得指定某个设备的图片,然后再删除。

RESTful 强调资源的唯一性,images/ID其实就是唯一的图片资源,而/devices/ID/images/ID也是设备的唯一图片,那么,我都不知道应该采用哪一个URI的设计。

/devices/ID/images/ID 返回 
image id

images/ID 返回具体的 image,此处的 ID 就是上面返回的 ID

也就是:
images/ID1 返回具体的 image 资源
/devices/ID2/images/ID3 返回 images ID1,此处的 ID3 是 device 拥有 image 的序号(第几张图片)。

引用cosmic的发言:

 

同样是这个问题

+1

引用Clarence的发言:

 

那么在api接口处该如何接收

 

将request数据中的字节转化为String,就是json格式的String字符串了。

一直不太明白,为什么要用http method来表示操作的类型,GET/PUT/POST/DELETE本身就不太好理解,表现力又有限。除了CURD之外,有时我们远程服务还得提供计算、校验等其他服务,决定用哪个method的时候,总感觉自己玩自己。

引用BTQ的发言:

一直不太明白,为什么要用http method来表示操作的类型,GET/PUT/POST/DELETE本身就不太好理解,表现力又有限。除了CURD之外,有时我们远程服务还得提供计算、校验等其他服务,决定用哪个method的时候,总感觉自己玩自己。


同问,reset api的原则或许适用于简单的资源类服务。不适于要求复杂的操作的业务场景。尤其,是需要对多个不同类的资源进行操作。还是觉得用SOA自然。

也许对企业应用,我们还是用返回json的soa风格好了

有个单词错了:Unprocesable 应该是Unprocessable

想请教一下关于资源的理解,我以前的理解是一个endpoint就是一个资源,每个资源的具体内容是由一条条数据组成的。因此对作者所说的:
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
就有质疑,我觉得应该是:
Get应该是从指定资源中取出一条或多条数据;
post对应的SQL命令就应该是Insert,而不是Creat。即向指定资源插入一条数据,而不是创建一个资源。
同样,put、patch、和Delete的操作也针对的是数据而不是资源。
不知道我说的对不对?

引用allen的发言:

@华仔_SuHua:

我认为设备图片(images)是一种资源,设备(devices)只是图片的不同形式的条件而已,所以应该设计为 /images/ID?devices={id}

我觉得有图片Id就够了,不同的服务器让图片Id不重复完全可以做到,或者图片Id=服务器Id-文件Id

@华仔_SuHua:

我也一直对这个感到困扰,希望哪个大神能给深入解答下

参考这篇写了用 Node.js 的实现 RESTful API 的练习。刚开始学 Node.js,不知道这样写是否正确,请大家给点意见,谢谢:

https://nodejust.com/node-js-restful-api-tutorial/

第三节 版本 中提到要在URI中加入版本号,但是另一篇文章(写于2011年的http://www.ruanyifeng.com/blog/2011/09/restful.html)却认为不应加入版本号。这是矛盾了吗?

====
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

====

疑问

RESTful中的resource 和数据库中的table是一一对应的吗? 如果是一一对应关系那么不就成了资源就是数据库中的数据了吗? 难道数据库存储的时候一定要按照资源的结构来? 而不是数据是数据,资源是资源? 
我认为数据库中的数据结构只是为了存储资源的时候能够更方便,更快捷,更灵活而且可扩展等,但是资源是业务数据,他不一定就要和数据库中的某个table对应,可以使一个table中的一条数据,也可以是多个表数据的集合或者数据库表中的数据经过抽象加工后的一种数据。

引用fallingwine的发言:

最后一条: "服务器返回的数据格式,应该使用JSON,不应使用XML" 存疑,返回某种自描述类的文本类数据即可,貌似没有规定不应使用XML;这与SOAP的rpc风格没有关联,两码事。

人家说的是避免没有不给使用!

引用skychf的发言:

 

人家说的是避免没有不给使用!

只是后来改了罢了

有个问题,资源的权限怎么在restful中体现?比如谁可以delete一个指定的雇员

关于URL中加入版本号很多人说跟之前作者说的不一致,好像楼主也没有说必须放在URL中啊,这篇指南跟之前那篇不冲突,“另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。” 只是推荐这样做吧!标准时说放在header里面,但是开发和使用还是放在URL里面更方便。

引用xiaoc的发言:

====
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

====

疑问

RESTful中的resource 和数据库中的table是一一对应的吗? 如果是一一对应关系那么不就成了资源就是数据库中的数据了吗?难道数据库存储的时候一定要按照资源的结构来?而不是数据是数据,资源是资源? 
我认为数据库中的数据结构只是为了存储资源的时候能够更方便,更快捷,更灵活而且可扩展等,但是资源是业务数据,他不一定就要和数据库中的某个table对应,可以使一个table中的一条数据,也可以是多个表数据的集合或者数据库表中的数据经过抽象加工后的一种数据。

我也有同样的感觉和认识

引用nebula的发言:

 

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

RESTful的API又不是专供浏览器端使用
别的HTTP客户端也都可以用啊,而且地址栏url很长并不会导致什么问题产生
即使你想把参数不加在url后面也是有很多办法的
ajax无论get post你都可以将参数放入http request body中
只不过,post方式http mime使得参数被设置为form data了
而get方式,变成了http payload
服务器端的处理上稍作改变就可以了
post的form data和get 方式url传参一回事
而get payload方式,你需要读取body然后自己解析一下,也不是什么难事

RESTful中,webapp跨域的问题有什么比较优雅的解决办法吗?

将版本号加入到URL中更方便直观,将版本号放在HTTP头信息中更简洁纯粹,个人推荐后者。

看了一些文档,现在REST架构一般是PUT用作新建,POST用作更新。
参照:http://restcookbook.com/HTTP%20Methods/put-vs-post/

引用HstarHu的发言:

看了一些文档,现在REST架构一般是PUT用作新建,POST用作更新。
参照:http://restcookbook.com/HTTP%20Methods/put-vs-post/

你真的看清楚了吗?PUT和POST都可用于创建,区别是PUT要有明确的资源地址

最近正好在用 swagger 进行设计,很方便,很多细节都由 swagger specification 自己处理了。

只是,最近发现自动生成的代码框架,有时候会有些 bug.

最近使用spring mvc,有一个疑惑。别人调我的接口,新建一个资源,需要传一系列参数,一些参数是用来判断我新建资源方式的。这些参数放在url上是否更加合适,按照我的理解,post请求的话,body里面存放的是新建的资源内容,而用来做判断的参数放在url上。这样的理解不知道合不合理,希望大神解惑。

简单明了,为什么网上很多文章讲不清楚?

怎么做用户登录 和注册等功能 = =。

引用ppp的发言:

怎么做用户登录 和注册等功能 = =。

关键词:
OAUTH 
token

阮老师 最近在看您关于REST的文章,我有一个疑问。您在《理解RESTful架构》一文中有讲到,不要在URI中加入版本号。
但是在本文中,在版本一节,您讲到应该将API的版本号放入URL。

我看到上面的评论对此貌似也有争议,不知道能否解答相应的疑惑,是否应该将版本号加入URL.

您好,我看你提到 API 的认证应该使用 OAuth2 框架。
OAuth2 框架适用于第三方应用对接到某个平台(比如github)并利用其资源提供额外服务,实际上也有利用 OAuth2 实现快捷登录的案例。
那么,内部多个子系统如果分开实现并各自提供自己的 API,那么该如何利用 OAuth2 实现统一认证呢,有没有一些模式推荐呢?谢谢!

引用j.w的发言:


开发人员一般都是chrome 或 firefox加插件调试

或者类似swagger之类的辅助工具

@andy:

比特币?

RESTFul API的书籍和您的博客中提到使用OAUTH做认证,但是却未披露细节。

OAUTH与自己实现Token之类的好像更加重一些。而且API的验证最好能够充分利用Cache(Memcached/Redis),否则RDBS容易成为系统瓶颈。

现在API设计中,API Key,API Secret,Token,Hash等词汇用得很乱。也希望您能够提供一些最佳实践和参考设计。

为什么用zoos而不是zoo呢?如果resource为单位,为什么不是单数呢?

评论比文章要长,说明作者的博客写得确实好

引用木驴的天空的发言:

评论比文章要长,说明作者的博客写得确实好

肚子里有干货,讲出来简洁易懂,评论才这么长吧

受益颇多,感谢!

post 和 put 定义得太绝对了..

确实是一篇好文章,巅峰了我以前的一些想法

引用童奇的发言:

注册、登录、登出这种api该怎么设计?
endpoint怎么定义,还有请求方法是用什么方法?

登录登出的资源是session
登录 POST www.xx.com/session 创建会话
登出 DELETE www.xx.com/session 销毁会话

网址中不能有动词,只能有名词。那么如果我做一个登录功能,允许两种登录方式,用户名/密码 和 手机/验证码,那么只做一个login接口的话,我是需要在后端内部先判断post过来的data里是否有 用户密码或手机验证码,之后再判断变量合法性?感觉后端更臃肿了。
按照之前的旧思路,只要做两个接口 loginByPassword 和 loginByCaptcha 就好了,没有那么多if,代码也会简洁很多。

弱弱的问下大师,支付宝开放平台的接口为什么没有遵循Restful,支付宝那么设计的目的是为了什么呢?

阮大大,我们服务端对支付那块设计是这样的,比如现在要发红包出去,就需要先调用充值接口,然后充值成功后再调用发送红包接口,他说这是遵循restful接口规范,我觉得这不是坑爹吗,各位怎么看,我觉的应该只需要一个接口(想要发红包的充值),服务端知道充值成功后帮我把红包发出去才对

引用石樱灯笼的发言:

网址中不能有动词,只能有名词。那么如果我做一个登录功能,允许两种登录方式,用户名/密码 和 手机/验证码,那么只做一个login接口的话,我是需要在后端内部先判断post过来的data里是否有 用户密码或手机验证码,之后再判断变量合法性?感觉后端更臃肿了。
按照之前的旧思路,只要做两个接口 loginByPassword 和 loginByCaptcha 就好了,没有那么多if,代码也会简洁很多。

如果遵循restful规范,假设登录成功的结果是建立一个session,那么接口可以分别定义为
POST www.xx.com/session?from=password
POST www.xx.com/session?from=captcha
不过感觉restful是指单一资源的状态转换,而实际应用中有很多情况很复杂,完全用restful表示实在是有点捉襟见肘,所以你的方法也可以,只要组织内部统一就可以了。

请问:有些浏览器不支持前端的DELETE PUT等方法,这个有什么规范的解决方案吗?

引用陈老师的发言:

阮大大,我们服务端对支付那块设计是这样的,比如现在要发红包出去,就需要先调用充值接口,然后充值成功后再调用发送红包接口,他说这是遵循restful接口规范,我觉得这不是坑爹吗,各位怎么看,我觉的应该只需要一个接口(想要发红包的充值),服务端知道充值成功后帮我把红包发出去才对

规范是死的,人是活的,在具体开发中,不可能完全依赖规范,你说的这个,根本不需要两次,两次请求会有事物问题。

引用BTQ的发言:

一直不太明白,为什么要用http method来表示操作的类型,GET/PUT/POST/DELETE本身就不太好理解,表现力又有限。除了CURD之外,有时我们远程服务还得提供计算、校验等其他服务,决定用哪个method的时候,总感觉自己玩自己。

是的,复杂的系统这个根本无法很好的设计,权限怎么控制?复杂的业务要操作很多种资源的时候url怎么体现?我觉得动作还是放在url上合适

Delete批量操作时,有什么比较认可的方式实现么。


普通分类: