RESTful 有用吗? HTTP 有 GET POST 就足够了?

2017-02-15 12:59:26 +08:00
 noli

不少程序员都是这么认为的,基于 HTTP API 的服务,只要用 GET 请求和 POST 请求就足够了。 像 RESTful 这样的 大量使用 PUT , DELETE 请求是不必要的。

真的吗,我来举一个例子。

假设有一类资源 ResourceXYZ ,对其有增删查改的操作。 如果只使用 GET POST 之类的设计方式,那么很可能会设计以下的请求接口:

POST .../addResourceXYZ
POST .../delResourceXYZ
GET .../getResourceXYZ?resourceId=resourceId
POST .../updateResourceXYZ

如果按照 RESTful 的 设计方式,很可能会设计以下的请求接口

POST .../ResourceXYZs
DELETE .../ResourceXYZ/{resourceId}
GET .../ResourceXYZ/{resourceId}
PUT .../ResourceXYZ/{resourceId}

现在假设,客户端要获取该资源,其 ID 为 resourceId 。 如果成功,那么一切都好说。 如果失败, Restful 的处理方式是,通过 HTTP status 返回错误码来表示原因,例如 404 表示该资源不存在。

那么只用 GET POST 两种方法的方式呢? 响应请求

GET .../getResourceXYZ?resourceId=resourceId

的时候能不能也用 404 呢?

按照 404 的语义,响应 404 是不对的: 因为客户端请求的 URL 实际上是正确的,只是对应的参数没有找到对应的结果 很多时候,就只能靠响应 200 然后返回空数据或者空对象来处理了。 例如 Content-type 为 application/json 时,可以返回 {} 或者

{
    "error": "not found",
    "code": 404
}

这样就会要求客户端,必须处理 HTTP 回复的具体内容,而不能只处理头部。 那么客户端要怎么处理这个 json 呢 要先解析 json ,然后尝试分别这是一个资源的内容,还是一个错误提示。

对于强类型语言例如 C/C++ OC Swift 写的客户端来说,恐怕就忍不住要问候服务端程序员一家了。

更重要的是……

没有库会支持这种拍脑袋式的设计。

全文完,欢迎拍砖。

42301 次点击
所在节点    程序员
207 条回复
wizardforcel
2017-02-16 14:53:40 +08:00
@noli

> 至于为什么 HTTP API 调用 HTTP API ,那是因为 do not repeat your self. 当这些被调用的 更底层 API 实现有变动的时候,你不需要改高层 API 的实现,虽然这也不一定是必须的。

HTTP API 的粒度太大,资源开销也大,服务端自己复用自己的时候,一般采用 DAO 的形式。看样子你根本不懂后端,就是一个架构太空人而已。
noli
2017-02-16 15:01:48 +08:00
@wizardforcel

天真。你以为 任何层的服务器应用都能调用 DAO 访问数据库吗?看样子你根本没做过严格的系统,都是搞些什么 login reg 之类的小社区吧
cnt2ex
2017-02-16 15:06:45 +08:00
顺便说一句, C/C++是弱类型
wizardforcel
2017-02-16 15:07:00 +08:00
@noli

99% 的 Web 系统都是这样做的。至于那 1%,你说这么大堆就是为了证明 REST 只是为极端情况下服务的? REST 真牛逼,呵呵,但是跟我没关系,我是这 99%。
noli
2017-02-16 15:21:19 +08:00
@wizardforcel

你说 99%的都是那么做我是信的,因为 99% 的公司都没有机会有上每天百万级别的 api 调用次数,要协调近百人的 team 来维持 api 服务。

算啦,知道你不服气。正面回应你吧。

你一直在说 http api 调度代价大,事实上数据库访问的代价更大,连接数基本上都是有上限限制的。数据库的调用逻辑更是测了又测。我扩张几台几十台 http server 简直是简单得不要不要的。再况且,我们这里背后的数据库是 mysql 代理,并不是什么 feature 都能支持,你随随便便搞个 DAO 还真不能保证 100%能用。

要是你选你也会选我一样的做法。
noli
2017-02-16 16:33:35 +08:00
@cnt2ex

你说得对, C/C++ 自身不是强类型的。但实际开发中我们很喜欢用它们的强类型特性。
所以不妨视作强类型的吧?
Mitt
2017-02-16 18:19:19 +08:00
服务器代价不代价的 能不能支撑几百万访问的 跟这个问题一点都没有关系吧 这种问题丢给架构师啊, REST 确实对于某些资源性特别强的时候要比只有 GET,POST 好用,但是很少啊,第一眼看上去什么都是资源,但是后期如果要对这个资源操作其他的事情,你就只能去改,把 REST 再改回 API ,你改了还不要紧,用你接口的也要改,还不如一开始就设计一套有风格又优雅的 API 接口,具备扩展性和优雅,还可以自己维护一套返回值类型,不必过度拘束 HTTP 的返回值,你说你如果又有 REST 又有 API 的话 客户端还得针对 REST 和 API 分别维护两套处理流程,何必呢?
noli
2017-02-16 18:29:07 +08:00
@Mitt

如果你的 API 使用者都是自己公司的,你这么做当然很 OK ,怎么任性也不会直接扣你钱。

但是重构这种事情,不是 RESTful 就有其他 API 就没有的。 而 RESTful 可以有版本,甚至你还可以给资源起一个新名字,然后将请求 forward 过去。善用 URL 就是有这么大的好处。

你自己设计一套 API 接口,要么按这个套路,要么不兼容以前的调用。
如果 API 里面没有 URL 没有版本这个概念,升级就够你烦的了。
那说来说去,还是说明 RESTful 的已经想得够多了,不像很多人以为的那么肤浅。
Mitt
2017-02-16 18:43:49 +08:00
@noli 其实你说的那些 REST 的好处 都是后端框架可以实现的 并不是指就非要以 REST 才有这种特性
Mitt
2017-02-16 18:54:33 +08:00
@noli 总的来说 纠结半天的 REST 和 API 的区别就是 请求格式和返回格式 两者, 而这完全看团队风格或者个人风格了, 所以不要在这上面总是纠结谁好谁坏了,这两个都是平等的,完全可以自己设计一套适合自己的 "RESTful"
noli
2017-02-16 18:58:11 +08:00
@Mitt

请你明确指出我说了哪些 RESTful 的好处,然后也符合你说的后端框架也可以实现的。

不过,退一万步来说。
RESTful 肯定不是蝎子拉屎独一份,但它是一个基于 RFC 的、有多种开源实现的规范。
我从来不反对谁谁用自己定制的协议和框架,
我只是讨厌那些对 RESTful 一知半解对 RESTful 的风格选择性遵守然后又把脏水倒到 RESTful 身上。

我想强调的是,如果你想清楚了,你怎么设计都可以。
如果你没有想清楚 ,多参考 RESTful 的设计以及这个设计是解决了哪些问题(很多人就是不理解 这些设计就来吐槽)
最后,如果你很懒,不想想那么多,那么直接按照 RESTful 规范来做,就是最省事的(很多人就是做到一半以为自己比 RESTful 高明又玩自己的一套)

这些人里面,国内程序员尤为严重,什么反范式啊反权威这样的话都说出来了。
好像他们自己的不幸就是他们口中的这些范式啊权威啊导致似的。
danielmiao
2017-02-16 20:25:42 +08:00
个人觉得, Restful 只是一个参考而已。。实际应用中还有很多问题,前后端调用只是一部分,涉及到微服务调用上情况会更复杂,举个 404 异常例子:
前后端分离情况:足够描述资源找不到的问题,对用户来说不管什么问题,都是没找到资源而已。
微服务架构:完全不够,造成 404 的问题,有可能是没有部署这个接口,也可能是负载均衡有问题,也可能是真的没这个资源,还有可能是参数组合有问题(也可以用 400 描述,但是 400 情况同样,具体不到哪个参数有问题)。
所以说, restful 只是一个风格而已,未必要完全一样,在 RPC 里很容易解决的问题,在 http 上可能有很多问题,不管是 ice , thrift , dubbo 可以定义大量的异常,同时不需要修改数据结构,但是到 http/restful ,没有异常这个概念,是需要定义量的错误码,还是复用原有的 http status ,我个人觉得还是自定义统一的错误体系会更好
noli
2017-02-16 21:47:34 +08:00
@danielmiao

首先,如果是负载均衡有问题,你们确定是返回 404 吗?
按我的经验,如果负载均衡是 Nginx 做的话,应该会是 503 或者 504 , 明确指出了这是服务端的问题。
如果是客户端方面得到了错的 URL 那么 就是 4XX 可能是 404 也可能是 405.
HTTP 的状态码本身就明确区分了责任方。

第二, RESTful 并不拒绝自定义错误码,只是必须要与 HTTP 状态码意义不冲突
事实上 AWS GCE 的 RESTful API 文档里面也定义了大量的自定义错误码。
习惯的做法是用 Json Envelope ,将适用的真正要返回的数据封装在一个公共知晓的结构里面。
怎么可以说 RESTful 没有异常的概念呢?

请确定你有了解清楚 RESTful 的内容,例如本贴中 #39 给出了一个不错的参考来源,其中就有提到:
“ Don't use an envelope by default, but make it possible when needed ”

这怎么能说它不支持异常呢?
讲道理的话, 3XX , 4XX 5XX 都能算作是异常。
danielmiao
2017-02-16 22:14:32 +08:00
@noli
我说的负载的问题是,如果你用 nginx 的时候,如果 ng 配置有误,会返回 404 。

如果是服务调用方的失误(写错接口,复数多 s 少 s 很正常)或者服务提供方修改了接口定义(实际遇到过,手误在写接口名时,敲 ctrl+s 未成功,直接写出一个 s 而不自知,单元测试不通过 http 调用,未测出来)出现 404 是没有办法和业务状态的 404 所区分,特别是服务更新或者出现问题,很难定位是部署出现问题还是业务本身问题,运维和开发如果分离的情况更复杂。

如果使用 json envelop 的话,实际上就是自定义错误体系,区分了 http 状态和业务状态。

宽泛的说错误码和异常的作用可以等同,但是上文特指 Exception ,请勿偷换概念
noli
2017-02-16 22:35:23 +08:00
@danielmiao

这种 NG 配置错误的问题,要影响服务 API 设计工作吗?很难发现或者测出来吗?
单元测试 RESTful 接口不通过 HTTP 调用……请问你们是怎么测?
如果上司让你们因为这些低级错误改设计的话那你换公司好了。

我再重申一下观点, RESTful 是工作在 HTTP 之上的,而且是与业务具体相关的协议
所以肯定是有 HTTP 状态和业务状态两个概念。
当然错误码也会有 HTTP 错误码 和 业务专有错误码,
HTTP 错误码是为工作在 HTTP 层的各路非业务设备看的
业务专有错误码是工作在业务端的,两者只要不含有意义冲突的定义。

转帖一下大厂怎么设计 RESTful 错误码

http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
https://developers.google.com/drive/v3/web/handle-errors
noli
2017-02-16 22:48:55 +08:00
@danielmiao

你的回复居然获得了一个赞,我很嫉妒,所以我再提供多一个简单的测试方法好了。
如果你想知道 404 是谁搞出来的。
curl -i 就可以看到是哪个服务器发出来的响应了。
真心不理解为什么会难测难发现。
cxe2v
2017-02-16 23:02:19 +08:00
@noli 那一个赞是我给的,你给出的两个大厂的例子明明就是自定义错误体系,怎么就变成了你宣扬的通过 HTTP STATUS CODE 来定义错误了,你不要偷换概念好嘛
noli
2017-02-16 23:30:33 +08:00
@cxe2v

请问我偷换了什么概念?


这是 AWS 的:

REST Error Responses

When there is an error, the header information contains:

* Content-Type: application/xml
* An appropriate 3xx, 4xx, or 5xx HTTP status code

The body or the response also contains information about the error. The following sample error response shows the structure of response elements common to all REST error responses.


这是 google 的:


The Drive API returns two levels of error information:

HTTP error codes and messages in the header
A JSON object in the response body with additional details that can help you determine how to handle the error.
The rest of this page provides a reference of Drive errors, with some guidance on how to handle them in your app.


难道我在 #115 表达的不是这个意思?
noli
2017-02-16 23:34:15 +08:00
@cxe2v 知道你是这种水平之后,我也不稀罕你的赞了。
cxe2v
2017-02-16 23:49:19 +08:00
@noli 嗨呀,我难得认真一次,下面的回答我从第一页看过来的,你自己回去看看你 50 楼以前的回复怎么说的

还有,你在 RESTful 规范不适合的时候强行用,借用其他“奇怪的技巧”?,我不得不说你这个 RESTful 不怎么 RESTful 啊
我水平太次,你技术大牛,你去安利你的 RESTful 风格吧,我继续用我的 GET,POST ,不知道做微信接口的同行看到了会不会自惭形愧,居然没有学习大厂用 RESTful

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/340607

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX