不少程序员都是这么认为的,基于 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 写的客户端来说,恐怕就忍不住要问候服务端程序员一家了。
更重要的是……
没有库会支持这种拍脑袋式的设计。
全文完,欢迎拍砖。
101
wizardforcel 2017-02-16 14:53:40 +08:00
@noli
> 至于为什么 HTTP API 调用 HTTP API ,那是因为 do not repeat your self. 当这些被调用的 更底层 API 实现有变动的时候,你不需要改高层 API 的实现,虽然这也不一定是必须的。 HTTP API 的粒度太大,资源开销也大,服务端自己复用自己的时候,一般采用 DAO 的形式。看样子你根本不懂后端,就是一个架构太空人而已。 |
102
noli OP |
103
cnt2ex 2017-02-16 15:06:45 +08:00
顺便说一句, C/C++是弱类型
|
104
wizardforcel 2017-02-16 15:07:00 +08:00
|
105
noli OP @wizardforcel
你说 99%的都是那么做我是信的,因为 99% 的公司都没有机会有上每天百万级别的 api 调用次数,要协调近百人的 team 来维持 api 服务。 算啦,知道你不服气。正面回应你吧。 你一直在说 http api 调度代价大,事实上数据库访问的代价更大,连接数基本上都是有上限限制的。数据库的调用逻辑更是测了又测。我扩张几台几十台 http server 简直是简单得不要不要的。再况且,我们这里背后的数据库是 mysql 代理,并不是什么 feature 都能支持,你随随便便搞个 DAO 还真不能保证 100%能用。 要是你选你也会选我一样的做法。 |
106
noli OP |
107
Mitt 2017-02-16 18:19:19 +08:00
服务器代价不代价的 能不能支撑几百万访问的 跟这个问题一点都没有关系吧 这种问题丢给架构师啊, REST 确实对于某些资源性特别强的时候要比只有 GET,POST 好用,但是很少啊,第一眼看上去什么都是资源,但是后期如果要对这个资源操作其他的事情,你就只能去改,把 REST 再改回 API ,你改了还不要紧,用你接口的也要改,还不如一开始就设计一套有风格又优雅的 API 接口,具备扩展性和优雅,还可以自己维护一套返回值类型,不必过度拘束 HTTP 的返回值,你说你如果又有 REST 又有 API 的话 客户端还得针对 REST 和 API 分别维护两套处理流程,何必呢?
|
108
noli OP @Mitt
如果你的 API 使用者都是自己公司的,你这么做当然很 OK ,怎么任性也不会直接扣你钱。 但是重构这种事情,不是 RESTful 就有其他 API 就没有的。 而 RESTful 可以有版本,甚至你还可以给资源起一个新名字,然后将请求 forward 过去。善用 URL 就是有这么大的好处。 你自己设计一套 API 接口,要么按这个套路,要么不兼容以前的调用。 如果 API 里面没有 URL 没有版本这个概念,升级就够你烦的了。 那说来说去,还是说明 RESTful 的已经想得够多了,不像很多人以为的那么肤浅。 |
110
Mitt 2017-02-16 18:54:33 +08:00
@noli 总的来说 纠结半天的 REST 和 API 的区别就是 请求格式和返回格式 两者, 而这完全看团队风格或者个人风格了, 所以不要在这上面总是纠结谁好谁坏了,这两个都是平等的,完全可以自己设计一套适合自己的 "RESTful"
|
111
noli OP @Mitt
请你明确指出我说了哪些 RESTful 的好处,然后也符合你说的后端框架也可以实现的。 不过,退一万步来说。 RESTful 肯定不是蝎子拉屎独一份,但它是一个基于 RFC 的、有多种开源实现的规范。 我从来不反对谁谁用自己定制的协议和框架, 我只是讨厌那些对 RESTful 一知半解对 RESTful 的风格选择性遵守然后又把脏水倒到 RESTful 身上。 我想强调的是,如果你想清楚了,你怎么设计都可以。 如果你没有想清楚 ,多参考 RESTful 的设计以及这个设计是解决了哪些问题(很多人就是不理解 这些设计就来吐槽) 最后,如果你很懒,不想想那么多,那么直接按照 RESTful 规范来做,就是最省事的(很多人就是做到一半以为自己比 RESTful 高明又玩自己的一套) 这些人里面,国内程序员尤为严重,什么反范式啊反权威这样的话都说出来了。 好像他们自己的不幸就是他们口中的这些范式啊权威啊导致似的。 |
112
danielmiao 2017-02-16 20:25:42 +08:00 2
个人觉得, Restful 只是一个参考而已。。实际应用中还有很多问题,前后端调用只是一部分,涉及到微服务调用上情况会更复杂,举个 404 异常例子:
前后端分离情况:足够描述资源找不到的问题,对用户来说不管什么问题,都是没找到资源而已。 微服务架构:完全不够,造成 404 的问题,有可能是没有部署这个接口,也可能是负载均衡有问题,也可能是真的没这个资源,还有可能是参数组合有问题(也可以用 400 描述,但是 400 情况同样,具体不到哪个参数有问题)。 所以说, restful 只是一个风格而已,未必要完全一样,在 RPC 里很容易解决的问题,在 http 上可能有很多问题,不管是 ice , thrift , dubbo 可以定义大量的异常,同时不需要修改数据结构,但是到 http/restful ,没有异常这个概念,是需要定义量的错误码,还是复用原有的 http status ,我个人觉得还是自定义统一的错误体系会更好 |
113
noli OP @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 都能算作是异常。 |
114
danielmiao 2017-02-16 22:14:32 +08:00 3
@noli
我说的负载的问题是,如果你用 nginx 的时候,如果 ng 配置有误,会返回 404 。 如果是服务调用方的失误(写错接口,复数多 s 少 s 很正常)或者服务提供方修改了接口定义(实际遇到过,手误在写接口名时,敲 ctrl+s 未成功,直接写出一个 s 而不自知,单元测试不通过 http 调用,未测出来)出现 404 是没有办法和业务状态的 404 所区分,特别是服务更新或者出现问题,很难定位是部署出现问题还是业务本身问题,运维和开发如果分离的情况更复杂。 如果使用 json envelop 的话,实际上就是自定义错误体系,区分了 http 状态和业务状态。 宽泛的说错误码和异常的作用可以等同,但是上文特指 Exception ,请勿偷换概念 |
115
noli OP @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 |
116
noli OP @danielmiao
你的回复居然获得了一个赞,我很嫉妒,所以我再提供多一个简单的测试方法好了。 如果你想知道 404 是谁搞出来的。 curl -i 就可以看到是哪个服务器发出来的响应了。 真心不理解为什么会难测难发现。 |
117
cxe2v 2017-02-16 23:02:19 +08:00 1
@noli 那一个赞是我给的,你给出的两个大厂的例子明明就是自定义错误体系,怎么就变成了你宣扬的通过 HTTP STATUS CODE 来定义错误了,你不要偷换概念好嘛
|
118
noli OP @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 表达的不是这个意思? |
120
cxe2v 2017-02-16 23:49:19 +08:00
@noli 嗨呀,我难得认真一次,下面的回答我从第一页看过来的,你自己回去看看你 50 楼以前的回复怎么说的
还有,你在 RESTful 规范不适合的时候强行用,借用其他“奇怪的技巧”?,我不得不说你这个 RESTful 不怎么 RESTful 啊 我水平太次,你技术大牛,你去安利你的 RESTful 风格吧,我继续用我的 GET,POST ,不知道做微信接口的同行看到了会不会自惭形愧,居然没有学习大厂用 RESTful |
121
noli OP @cxe2v
反正 v2 上发了帖就改不了。你愿意指出具体问题你就说,不想认真就别回。 你要说 RESTful 规范不适合的时候强行用,真是好笑了——我做了什么 RESTful 的系统,又是怎么不适合的? 或者我举了哪个例子,让你觉得 RESTful 不合适? 怎么不合适你又不说,不好意思,不是你女朋友你老婆,我不负责体贴你。 既然我举的例子你不服,觉得微信是王道,你说出个微信的道理来,有没有理大家评评嘛。 觉得说不过、不值得提前撤,也是可以的。 接受我的奚落就是了。 |
122
danielmiao 2017-02-17 00:30:41 +08:00 2
@noli
本来我是不想回的,因为这种脱离生产的为辩论而去做辩论,没有特别的意义。但是看到你这么酸的。。。。。 我从主贴上看到您是驳斥这种 Json Envelope ,你希望通过只处理 http 头来解决问题。。。但是后续又提出“ HTTP 错误码是为工作在 HTTP 层的各路非业务设备看的 ”,那我的理解就是关于资源有无,应该是业务层,而非 HTTP 层的。。所以真心不知道你是什么观点。 至于你说的:“ curl -i 就可以看到是哪个服务器发出来的响应了。 ” 我觉得没问题,能解决问题,但是,如果不用这些方法,只是通过日志直接能判断服务的问题,这样的效率不是更高,只是为了遵循某个规范,而搭进去排查运维、开发的人力和时间成本毫无意义。 至于你提的 2 大厂。。。只是代表了一种设计思想(错误码和 json 同时使用),但是并非强制标准抑或其他。 国内几大厂以及主推微服务架构的 Netflix ,则推行的则是 HTTP 状态只负责链路层,业务错误由另外结构体处理。 从 spring-cloud-feign 上来看,目前的方案是:非 200 的错误的处理方案是统一的 errorDecode ,也就是非 200 由处理链路层的方法去统一管理。 所以说不管方案是不是纯血的 Restful ,贴近生产,符合实际,能提高劳动效率的方案才有意义吧。 |
123
noli OP @danielmiao
我知道你为什么会这么理解我的观点了。 明确一下我的观点: 0. 我反对只用 GET POST 1. 如果只用 GET POST ,那么 GET 一个资源 ID 错了的时候用 404 会有歧义 2. 如果不希望客户端错误理解 404 (并且将错就错),那么返回 200 和 一个 JSON 应该是可行的做法 3. 可是这样就必须强迫客户端总是要解析 JSON ,并且如果真的是资源不存在的时候, HTTP 设备也无法知晓 所以 3 就只用 GET POST 这种方式带来的隐忧,也因为如此我反对这种设计。 |
124
halden 2017-02-17 01:48:47 +08:00 1
做前端的,表示 RESTful 这个东西应该是为了前端方便而来的。你可以说它是风格,但我觉得 RESTful 作为不是硬性的“标准”可能更加适合,因为我肯定希望后端全部按照这套东西来写 api ,这样前端可以用 js 根据不同的 code 做相应的反馈,状态码都是固定现成的,一套 js 只需要稍作修改就可以用在其他地方
create(post) - return 201 / 409 delete(delete) - return 200 / 404 edit(patch) - return 200 / 204 /404 read(get) - return 200 / 404 replace(put) - return 200 / 204 / 409 这一套可以应付大多数情况了,而且 201/204/409 的使用显然比单纯用 200/404 来得人性化 我看楼上有人举『移动到文件夹 X 』的例子,很明显这个已经超出了 RESTful 所定义的范畴,你强上 RESTful 肯定不合适,但比如你 增删改查 设计成符合 RESTful 的架构,然后其他的再自定义这完全 OK 啊 |
125
lightening 2017-02-17 06:22:22 +08:00
@magict4 非常建议有空的时候真的看一下。 dhh 作为 Ruby on Rails 的设计者,很多思想都非常富有远见。而且他不像很多大牛一样是极端。他先介绍了 REST 风格到底给你带来了什么好处,也说了在什么情况下需要 make exception 。
|
126
cxe2v 2017-02-17 09:06:58 +08:00
@noli 第一啊,我并没有说你做的什么系统有什么问题,只是你自己前后矛盾而已
第二,可能你语文不太好,不能体会字面背后地意思,那我现在说清楚,不要为了 RESTful 而 RESTful ,要看实际情况来 第三,微信的例子,就用我最近要用到的微信公众号接口来说吧,它也并没有采用 RESTful 风格,而是定义了一个确定的 JSON 结构,然而我感觉在你心目中微信根本不算什么,在你眼中,要每天百万级的接口调用才叫牛逼 还是那句话,你开心就好,我真是闲得蛋疼 |
127
juice 2017-02-17 09:28:57 +08:00
看看 fb 的 Graphql ,也是不错的一个解决方案
|
128
noli OP @cxe2v
1. 前后什么内容什么观点矛盾了请指出。要不你看看 #123 是不是你自己误读了。说话说一半神惹人烦 2. 什么实际情况请说明,别装 B ,说话说一半神惹人烦。 3. 微信的接口确实没啥牛逼的。各家 sns 的 api 封装是我曾经做过的产品之一,各位写移动 app 调用的 sdk 可能有可能包含我的代码。所以我觉得我起码有调查过,你说它牛逼就因为它是微信? |
129
wizardforcel 2017-02-17 10:06:55 +08:00
@noli
> 你一直在说 http api 调度代价大,事实上数据库访问的代价更大,连接数基本上都是有上限限制的。数据库的调用逻辑更是测了又测。我扩张几台几十台 http server 简直是简单得不要不要的。 就算真的要用集群,就必须用 REST ??呵呵。你们家 REST 还是跟集群绑定的?? REST 的发明者都没你这么强硬吧??你是来布道的,但是大家的选择权也是很重要的。 REST 虽然优雅,但是我不喜欢就可以不用。 |
130
wizardforcel 2017-02-17 10:29:36 +08:00
终于扯到返回信息的处理了。。
[按照 404 的语义,响应 404 是不对的: 因为客户端请求的 URL 实际上是正确的,只是对应的参数没有找到对应的结果 很多时候,就只能靠响应 200 然后返回空数据或者空对象来处理了。 ] 你前面也说了,自定义错误码就能解决。(但是国内多数厂商提供的 API 是 200 + errmsg 这种方式,没你想象的这么简单)。 [这样就会要求客户端,必须处理 HTTP 回复的具体内容,而不能只处理头部。] 就算拿 HTTP 状态区分,对于成功的请求,还是要解析 JSON 的。反正 JSON 这块你绕不过去。 [对于强类型语言例如 C/C++ OC Swift 写的客户端来说,恐怕就忍不住要问候服务端程序员一家了。] JSON 库的封装跟强不强类型没关系,跟是否存在反射有关系。 OC/Swift 的 Runtime 十分强大,所以把 JSON 变成对象是没问题的。 至于 C/C++ 嘛,在互联网的服务端 /客户端中占得比重太少了,我设计个接口为啥要考虑它??那我是不是还要考虑一下 Delphi 、 COBOL ?? |
131
noli OP @wizardforcel
谁强迫你了?谁布道了? 我在批评乱改 RESTful 规范然后又搞出自以为高明的设计。 选择权很重要? 我们不聊政治,按需遵守规范就是降低效率浪费人力浪费金钱,我觉得这些比什么鸟什子选择权重要得多。 |
132
wizardforcel 2017-02-17 10:36:14 +08:00
@noli
你忽略了我上面那句话。 mysql-cluster 是跟 REST 绑定的?? redis-cluster 是跟 REST 绑定的?? 谁告诉你用集群就必须用 REST 了??数据库连接背后就不能是集群?? |
133
noli OP @wizardforcel
我说了很多次,也贴了 Google Amazon 的做法,我不介意再重新说明一次我的观点: *** 自定义错误码是不够的,必须结合 HTTP STATUS *** 而我帖子里面举的例子, GET POST 返回 200 然后 json 说有错这种做法,就是违背 HTTP 语义乱来的做法。 情形就好像 TCP 里面明明发了 ACK 却想要对方重新发送一样鬼扯。 “至于 C/C++ 嘛,在互联网的服务端 /客户端中占得比重太少了” 当我说客户端的时候,你想到的是手机、浏览器; 而我想到的还有 Proxy SERVER , CDN Server 你只关心业务,我关心的是整个网络上的机器。 所以视野不同,结论不同。 如果算上我说的,你还会认为“ C/C++ 在互联网的服务端 /客户端中占得比重太少”吗? |
134
noli OP @wizardforcel
0. 数据库机器比 HTTP SERVER 价格贵得多。 1. 当我增加面向客户的机器时,如果这些机器是纯粹的 HTTP SERVER ,那我就不需要考虑调整数据库集群,但同时带来更好的处理 客户请求的响应性能。 2. 如果这些 HTTP SERVER 我能确保它们实现业务的时候,正确理解和使用 HTTP STATUS 语义,例如 GET 是幂等的, 200 就肯定是成功,这样的语义,那我就能确信它们能降发挥 CDN 的作用降低数据库压力。 3. 遵守 RESTful 的规范确保 这些 HTTP SERVER 能够满足 2. 所以没错,事实就是这么直接,信不信由你,现行的标准里面 RESTful 在集群上就是这么有效果。 |
136
baconrad 2017-02-17 14:32:08 +08:00
```
「如果你有的只是一個鎚子,那麼所有的東西看起來都像一個釘子」 「把一個熟悉的技術或理念強迫的應用於大量的軟體問題上」的概念已經被認為是一種反模式,一種編程時應該避免的實踐 ``` https://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail |
137
Cbdy 2017-02-17 15:04:41 +08:00
“不少程序员都是这么认为的”,第一句话,谁这么认为?
|
138
noli OP |
140
gzq527 2017-02-17 15:10:54 +08:00
这是个找喷贴???
|
141
uzumaki 2017-02-17 15:14:36 +08:00
秀优越 秀大神还是找喷?
|
142
hysterin 2017-02-17 15:37:03 +08:00
讲道理,我是来学习撕 b 技巧的。
真的受教了。感恩各位大神。 |
145
neurocomputing 2017-02-17 15:45:58 +08:00 1
瞧你说的 HTTP 其实也不需要了
直接 raw TCP 通信呗 开头写上资源和动作 后面跟参数就行了呗 |
147
ijustdo 2017-02-17 15:56:41 +08:00
我咋看都觉得应该是 nginx rewrite 该干的事
|
148
xcv58 2017-02-17 16:03:21 +08:00 via iPhone
|
149
uzumaki 2017-02-17 16:16:42 +08:00 via Android
@silva 虎躯一震,王八之气一发,只要你说就有机会,不说,机会都没有,再说了,你不知道他是谁而已
|
153
noli OP |
155
hysterin 2017-02-17 16:57:35 +08:00
@noli
不不不,您误会了,先跟你道个歉。这里不是来歪楼的,确实是表达敬意。只是可能态度不太端正。 事实上我是做品牌的,您的回复全程我都有关注。 虽然内容准确与否有待商榷,毕竟是争议性话题,而且大家各有立场,无法说出个绝对正义。但是您的回复,非常精彩,公关级的撕逼。除了有几楼稍微过激之外,真的是很厉害。 ---------------------------------------------------------------------------------------- 顺便回复 @silva 小伙伴,悄悄跟你说,别说论坛了,我曾经认识过一个妹子还能在淘宝约。。平台不重要,重要的是你想不想约。。想约就总有上钩的人的。。。 |
156
baconrad 2017-02-17 17:27:56 +08:00 2
@noli
那兩句話只是想提醒自己,不要再犯同樣的錯誤。 兩年前看到 Restful 的設計方式後,真的覺得自己撿到了一塊寶,興奮地和當時合作的後端介紹它的規範和理念,然後討論著該如何導入之後的項目裡。 但以結果來說我錯在太堅持,全部依賴 Restful 設計原則,導致任何需求必須把它捏成 Restful 能夠接受的樣子,還得告訴自己 Restful 果然很棒,什麼都做得到。 「當時的我們就像拿著鎚子的小孩,所有的東西都想拿鎚子來敲。」 Restful 可以解決大部分的需求,我同意, 但不是所有需求都得用 Restful 解,即使它做得到。 離題了, 但這是那兩句話的原意。 |
157
silva 2017-02-17 17:30:19 +08:00
|
158
uzumaki 2017-02-17 17:46:43 +08:00 via Android
@noli 事实就是 当你用如果是纯粹的 xxxx 来考虑,就违背了现实因素。我还纯粹数据库呢。。要你这有何用
|
159
bloomy8 2017-02-17 17:55:18 +08:00
这帖子太精彩了,忍不住冒个泡
|
160
Nitromethane 2017-02-17 21:15:08 +08:00
Restful 只是 http 这一层面的~
客户端浏览器使用 restful ,或服务端使用 restful ,模板都是解决工程问题。 服务端调用其他系统的借口,当然还是习惯 RPC 和 Thrift~ |
161
bravecarrot 2017-02-18 00:25:34 +08:00 via iPhone
其实从模块儿设计的思想来说
restful 是符合 高内聚,低耦合 的 就拿 lz 的例子来说, url 分发的模块和逻辑处理的模块应该尽可能的分开,你设计了 add del modify and so on ,但是业务逻辑更多的时候呢,需要修改的时候怎么办?从 url 到逻辑代码都要修改。 采用 restful 的方法,都到一个地址 后端的人想怎么写就怎么写,不同的人写不同的模块,都处理 json 就行了。功能增加,功能修改都只需要改后台逻辑部分就行了 最开始做东西 就按 lz 的思路做的,每一个操作都一个 url ,工作量非常大!后来了解到 restful ,简直是艺术 |
162
bravecarrot 2017-02-18 00:28:08 +08:00 via iPhone
搞错 lz 观点 尴尬😅
|
163
LINAICAI 2017-02-18 00:50:05 +08:00
然而大部分后台开发人员都不按照这个来,几乎都只有 post 和 get 。
|
164
thekll 2017-02-18 02:02:36 +08:00
RESTful 是一种针对互联网应用系统的架构风格( architectural style ),有非常明确的应用场景及要求。
REST 化对系统性能、灵活性、统一接口、移植性、可靠性等方面都有好处。 如果不以系统架构(客户端、服务端)的角度考虑问题,只考虑服务端内部如何方便实现,不考虑客户端,是没办法理解为什么要 RESTful 的。 反对的,也应该拿出点干货。 有些根本就不知道 RESTful 的目的。 |
165
lygmqkl 2017-02-18 11:42:07 +08:00 via iPhone
第一 如果一定要使用一个一定是 post ,而不是 get ,所以精简一些 post 就足够了。
第二如果你觉得 get and post 对你同意重要,那么你肯定需要 put delete and option 。包括 resource 和幂等在内的一些概念真的很棒! 最后国内有太多伪 RESTFUL ,合作过一些大项目出来的 a or i 工程师简单粗暴的觉得 post 可以代替 put 也是醉了 |
166
smallpath 2017-02-18 12:44:10 +08:00
百万级别的 api 调用次数很多?/手动 AC 扇子脸
|
167
lslqtz 2017-02-18 13:35:26 +08:00 via iPhone
post 同样可以理解为向服务器传递消息,通过参数来说明动作
实际上,普通的带参数 get/post 单个请求能完成的 不应用 restful 多次请求增加负担 优雅是一方面 负载同样重要 |
168
noli OP |
170
noli OP @lslqtz
我只是举了在我那个系统里面的做法为例子,并且是针对带有事务性质的要求,用基本动作的组合来描述一个事务。 没有人说过 RESTful 语义做那种看起来是多个请求组合的方式就真的一定要发多个请求。 既然你可以说的出“ move copy 等请求” 想必用 RESTful 语义 POST a move POST a copy 也不是什么难题吧? |
171
Balthild 2017-02-19 01:35:42 +08:00 via Android
@noli 抱歉啊,你又找错人了。
你应该找一开始说「只是要优雅」的人去要优雅的定义,毕竟这句空话是他先说的。而且,我看你好像还回复了他来表达赞同,所以我相信你知道我说的是谁。 邮件的例子,你举是举了,然而却搞出一堆什么事务啊队列啊之类的东西,越搞越复杂。如果不用 REST ,就是传一个自定义动词 move 的事情,显然比 REST 更优雅。 |
172
Balthild 2017-02-19 01:43:07 +08:00 via Android
手里拿着锤子,看什么都是钉子。
殊不知排钉得用气钉枪打,用锤子只会把它锤弯。 |
173
noli OP @Balthild
那是因为我给了一把索尔的锤子。当然可以把所有东西当钉子了咯。 事务和队列确实是业务需求,跟 RESTful 无关。 因为你不知道实际上我做的事情是用 json 做了一个 DSL 来描述事务。 这的确跟 RESTful 没有直接的关系。 但如果不是 RESTful 的方式解构所有资源操作,你根本就不可能像我那样用 JSON 来描述所有的事务动作。 你必须一个一个事务地去实现而不是像我写的那个系统那样,把要做的事务交给调用者来定义。 跟你们这些靠勤奋的锤子比,我都不好意思了。 |
174
Balthild 2017-02-24 10:56:28 +08:00 via Android
@noli 你看,你这样不就是不优雅吗——对于前端而言,事务的概念本应是不可见的,而你却为了遵守 REST 强行把锅甩了。
|
175
noli OP @Balthild 我来指出你思考问题过程中的一些缺陷请你不要介意:
1. 为什么“对于前端而言,事务的概念本应是不可见的”? 这是一定的吗? 这是业务决定的,还是只是你的信念?如果事务是客观存在的,前端可以有什么办法视而不见吗? 2. 即使 ““对于前端而言,事务的概念本应是不可见的” 是成立的, RESTful API 就做不到让前端不觉察事务的存在吗? 3. “甩锅”这种事情,请问跟是否 RESTful 的关系是什么?难道你认为,假如有人想把锅甩给服务端,服务端可以用 要遵守 RESTful 来拒绝吗? 你看,你的论点基于很多你个人的经验和没经过深入思考的假设。 没错,如果遇到你这样的同事,我就可以墙裂推荐 RESTful 来甩锅,毕竟写 RESTful 规范的人比你牛逼。 有本事你也写个规范出来让大家讨论讨论,光坐在一旁拆台不体具体建议,这种事情也不是特别难做吧。 |
176
Balthild 2017-02-24 14:23:17 +08:00 via Android
@noli
1. 首先现在为什么前后端分离是趋势?那就是因为前端本应只考虑对资源之操作的动作,而不应考虑操作的实现细节。事务的概念已经属于实现细节了,因此不是前端该考虑的。 2. RESTful 的 API 只有四个动词,所以对于动作的描述能力极其有限。 3. 为了在不方便遵守 RESTful 的地方遵守 RESTful(请注意这个前提)而拒绝实现某种方法,称为甩锅的确不准确,应该直接称之为设计缺陷(这个词针对 API 而不是针对 RESTful)。 在上面的例子中,对邮件的操作你可以放一个事务,里面的细节是删除再新增,那么如果是对一个几 GB 的对象的操作呢? |
177
Balthild 2017-02-24 14:28:23 +08:00 via Android
@noli 至于我设计的 API 风格肯定没有 REST 的牛之类的话语,这属于转移话题。原本在讨论 REST 风格好不好,你却转移到我设计得好不好。
|
178
noli OP @Balthild
1. 请问新浪微博 API Twitter API Facebook API 这些,前后端分离在哪里?他们是不是 RESTful ? 再强调一遍。前端不行是前端的事;前后端分离是前后端分离的事;前后端分离之后如果有事务,即使不用 RESTful 前端一样要有事务的概念。 RESTful 能解决这些问题,但并不是只解决这些问题,你拿这些来说 RESTful 不行是因为 你不行。 2. 可是使用 RESTful 的人可以有无数种抽象出资源的方式。显然你不行,所以你用不了 RESTful 。 3. 删除 GB 级别的操作又怎么样。你写代码的时候,知道有异步操作,知道可以用 Promise 或者 Task ,怎么在设计 RESTful API 的时候就不懂设计 Promise 或者 Task 资源? 这是你蠢还是 RESTful 蠢? |
179
noli OP @Balthild
说你只懂拆台不懂具体建议,是因为 RESTful 有现成的例子所以你可以发出数不尽的批评而不需要意识到自己的不足。 我之前已经说过了,只要你想清楚了,你可以再做一份自认为比 RESTful 更好的规范。如果没有, RESTful 本身是一套很好的规范。从上下文来看,你批评 RESTful 的地方恰恰都是你没有想清楚的地方。 没错,我是想转移话题——你还没够资格批评 RESTful 哪里不好,或者没有到点上。 |
180
Balthild 2017-02-25 16:36:39 +08:00
@noli
1. 如果不用 RESTful ,前端可只考慮不可分割的「動作」之概念。每個「動作」對於前端而言都是原子性的。 2. 既然事務也可以抽象成資源,那何不把整個 API 改成這樣:原始的資源能 GET ,其他對資源的操作皆 POST 至 /transaction 。這樣不還是只需要 GET 、 POST ,不需要 PUT 、 DELETE 。反正事務也是一種抽象的資源,這很 RESTful 啊~ 3. > 删除 GB 级别的操作又怎么样 我說的不是刪除,是移動。難道你移動一個幾 GB 的對象時,是先刪除它然後再於其他位置創建?難道為了套 REST ,是可以連效率都捨棄的? |
182
noli OP @Balthild 说你不够资格并不是否定你评论的权利,只是想让大家以及你确定,是否在浪费时间。然而现在看来你确实是浪费时间。
1 你当然可以设计这样的规范,并且或许已经有这样的实现。但是并没有普及。我已强调过了,有现成的规范你不妨介绍给大家。 但是,“动作”本身就是业务的一部分。如果真的有这么一份规范,并且每个动作都形成具体的规定,我预感它要遵守的规范也好习惯也好不比 HTTP 动作简单。 再者,”动作“本身是不是一定要有原子性语义,这还是业务决定的。所以你的提议听起来感觉就很临时。 2. CRUD 是普遍存在的需要。把 CUD 三类请求放在了 POST 里面不是不可以,但这样你又必须把动作写在业务代码里面,既然这样,为什么不直接用 HTTP 语义? 再往深了说,非纯函数式编程的语言里面,哪个语言没有这个过程:定义符号(变量)-> 读取/修改符号(变量) -> 符号(变量)离开作用域。 POST GET PUT DELETE 恰好就是这个过程的对应。换句话说,遵守 RESTful 规范,你的系统潜在地拥有了一个描述符号演算系统的能力,这比你什么鬼“动作”有根据多了。 3. 脱离业务实现谈效率是扯淡。 “自身水平沒有欲批評的對象高,便無資格作批評” 不解释。 |
183
Balthild 2017-02-28 20:31:47 +08:00 via Android
@noli
2. 你理解错了,我举这个例子只是为了讽刺你这这种「能用那四个动词描述的就用那四个动词,不能的就视作事务」之做法。 既然依你所言,事务可以视为资源,同时又有一切操作都可以被事务包括之前提,那么 API 便设计成甚至只用 POST 都可以,而且这还能同时遵守 REST ——这才是我想表达的讽刺语义。 3. 我举出例子来说明用 DELETE+POST 来实现 Move 动作的荒谬性,本身就是为了指出你脱离具体业务之错误。 |
184
noli OP @Balthild
你非要认为移动是特殊的动作。业务许可的话,难道就不能一个 Put 或者 Patch 请求解决么。把资源的位置视作一种扩展属性,这样不 RESTful 么?这根本不是什么本质上的缺陷。 我已经解释过了,有很多种抽象的方式和角度,但是 RESTful 大多可以满足。要做成事务还是两个请求还是一个请求,看具体需要。视乎系统怎么设计有什么需求。 你非要用你死板而缺乏想象力的方式来想象我使用 RESTful 的方式,我只能认为这是不断给我打你脸的机会。 |
185
Balthild 2017-03-03 13:58:03 +08:00
@noli
2. 这当然很 RESTful ——我前面已经讲了。我是指出,这种方式和其他只使用 GET/POST 的 API 风格并没有本质上的区别。 3. 终于改口了啊,现在你说「 RESTful 大多可以满足」。可之前你说的却是「除了无限的东西外其他东西都可以抽象成资源」「抽象不成是你水平低」。 我本来就在强调看需求,不顾需求强行 REST 的人是你。 我死板地想象,这我千真万确地承认,所以建议你不如明白地解释解释,你是怎么用包含「删除+重新创建」的事务「移动」一个几 GB 的资源的。 |
186
Balthild 2017-03-03 13:59:38 +08:00
@noli 请问如果把资源的位置视为扩展属性,用 PUT 一个 Patch 来解决移动操作,就不需要专门实现一下吗?别跟我说你内部还是用「删除+创建」来处理的。
|
187
noli OP @Balthild
2. “用 PUT 或者 PATCH ” 当然和 “用 GET / POST ” 有本质的区别。 我肯定你只是一直在 blah blah blah 而没有看到过我前面说过的区别,区别在于:如果 PUT 或者 PATCH 失败了,你可以通过 HTTP STATUS 知道是资源不存在还是 API 调用有错误,但你只用 GET / POST 没法保证只需在 HTTP 层解析就知道是什么错误。 这个问题就好像你调用 int GetNumber() 这样的接口,如果返回 -1 你无法判断这是一个错误还是一个结果。 而现代的做法应该是类似于 Either<int, Error> GetNumber() . 你要说前者足够你的业务使用就算了,这没问题,你喜欢就好。 但非要指责抽象形式 Either<int, Error> 是死板,我只能说这是井底之蛙夏虫语冰。 3. 你倒是找一个 RESTful 做不出来的需求啊…… 抓文字游戏胜利了显得你很聪明? “ PUT 一个 PATCH ” 这种说法让我发现回答你真是浪费时间,原来你连 RESTful 除了 PUT 之外还有 PATCH 都不清楚啊…… 你觉得你还好意思跟我 讨论 RESTful 不行么? 自己去看吧,不用谢: https://tools.ietf.org/html/rfc5789 移动一个资源怎么用 PATCH ?简单: PATCH .../HugeResource/1234567 {"oldPath": "../old/path/to/resource", "newPath": "../new/path/to/resource" } 不改变资源的物理位置,改变资源挂载位置,这是不是移动?嗯? 我都说了,只要你的业务可以设计成这样,就可以支持这样的操作,跟 RESTful 一点关系都没有 难道你只会用 cp 和 rm 不知道有 mount ? |
188
Balthild 2017-03-04 13:41:51 +08:00
@noli
2. 类比错误。 用 HTTP StatusCode 来表示错误才是相当于 int GetNumber() ,且用返回之负数的值来区分错误。 <int, Error> GetNumber() 则是类似于返回 json 来描述错误。 3. 哈哈哈哈哈哈哈哈哈哈哈哈 REST 提出之后十年才有 HTTP PATCH 方法呢…… 你看,你为了套 REST ,你又要特意增加一个「挂载位置」的概念。为了区分物理位置和挂载位置,整套系统的复杂度就增加了。况且在意义上,这和专门实现一个 move 动作有区别吗?哦,我是指「是否符合 REST 之外的区别」。 |
189
noli OP @Balthild
1. 不明白为什么是“类比错误”。 为什么你认为 “用 HTTP StatusCode 来表示错误才是相当于 int GetNumber() ,且用返回之负数的值来区分错误。” 以及“ <int, Error> GetNumber() 则是类似于返回 json 来描述错误”? 没说理由,你太着急反驳我了吧。 2. 你既然有“移动资源位置” 的概念,那肯定本身就有“资源存储位置” 的概念,或者“资源挂载位置” 的概念,两者之少有一个。 所以不存在你说的,“为了套 REST ,你又要特意增加一个「挂载位置」的概念”。 不知道你有什么好装 B 的。 * 针对只有“资源存储位置”概念的系统 -> 做成事务封装两个请,求或者直接发送两个请求,或者再包一层 API 来决定要用哪种方式 总之,物理上改变资源存储位置肯定是两个, API 形式上是一个调用,只不过是选择不让人看到这个事实 ;暴露两个原子的调用接口的 API 设计选择,是选择让调用者知道更多。 这只是一种口味问题,你把这种问题偏离到 “套用 REST ” 只能说明你根本没仔细想过。 * 针对支持“资源挂载位置” 概念的系统 -> 做成修改资源挂载位置的请求 。 支持以上这些, RESTful 都是毫无问题的。 --- 为什么我认为 "用 HTTP StatusCode 相当于 返回 Either<int, Error>", 我的理由是: 1. 对所有的使用者来说,结果的理解方式是唯一的,要么 match(int), 要么 match(Error) 2. 对所有获得 HTTP Response 的使用者来说,要么 match 到他关心的 HTTP Status Code ,例如 2XX, 3XX, 4XX 要么 match 到它可能不关心的 match(Error) ,于是处理这个错误时可以不用管 Error 具体是什么(具体错误内容在 Http Response Body 里面,通常是 Json ),这些通常是非业务相关的中间设备,例如缓存服务器, HTTP 网关之类的; 也可以是业务相关的设备,这些设备可以进一步处理 Error 的解析(也就是解析 Response Body 里面的 Json ) 为什么我认为 只使用 GET POST ,相当于返回 int , 就如同我本帖一开始说的那样,相当于要求所有参与调用这个 API 的人知道, int 的含义并不是简单地一个数值,而是在某个数值范围内才是正常的结果,在某个数值范围内是表示错误。 只使用 GET POST , 如果不是一直使用 200 ,那么就有混淆资源相关错误和 API 错误的问题; 如果一直只使用 200 ,那么就强迫所有使用者必须了解业务细节才能理解 HTTP Response ,对有 HTTP 中间非业务相关的设备来说,肯定是不友好的。 当然,很多系统没有大到拥有非业务相关的 中间 HTTP 设备。 所以对于这种系统,这是一种传统约定的偷懒行为,能 work ,但并不是什么好的设计。 这样的系统一旦扩展,需要添加业务不相关的 网关、缓存之类的设施之后,就会因为 API 设计的偷懒 而 要复出 重构的代价。 |
190
Balthild 2017-03-07 11:12:39 +08:00 via Android
@noli
1. 相反,如果不一直使用 200 ,才会导致 API 自身错误和资源相关错误。此时如返回一个非 200 的 HTTP 状态码,调用者会无法判断这个错误是否经过了 API 的业务逻辑才返回的。所以我的做法是经过了业务逻辑才返回的错误放在返回的 json 中而不是 HTTP 状态码——这就是为什么我认为<int, Error> GetNumber() 是类似于返回 json 来描述错误。 2. 不把位置视为资源的扩展属性,则资源的逻辑位置和物理位置可以用一套自然的对应法则进行对应。否则,你必须手动实现物理位置和逻辑位置的非自然的对应关系——通常得把它写进数据库,资源被访问时去查询。 |
191
noli OP @Balthild
1. “调用者会无法判断这个错误是否经过了 API 的业务逻辑才返回的” 很明显这是因为没有正确地使用 HTTP Header 。 调用者只需要分析一下 Content-type 和 Server 头就知道是否经过 API 的业务逻辑。 但说实话,作为 API 调用者的 HTTP Client ,知道是否经过 API 调用这真的重要吗? 这是否意味着调用者与 API 服务之间的过度耦合? 如果确实未经过 API 服务就得到了 HTTP 回复难道调用者就不需要处理了吗? 所以这是一个因为错误的做法而产生的伪问题。 错误的根本原因就在于对 HTTP 的能力了解不足,才会说只要 200 和 GET POST 就够了。 2. 什么叫做“自然的对应法则”? 我不觉得你说的“自然的对应法则” 不适用 “存储位置” 和 “挂载位置” 两种概念中的一种。 你能够 “不把位置视为资源的扩展属性” 那纯粹是业务不需要。 拿 “邮件”作为资源的具体例子,对应“发送”“归档”“删除”“移动”。 你解释一下你的所谓 “自然法则”是什么。 反正我是觉得肯定跑不开 “存储位置” 和 “挂载位置” 这两种。 “存储位置”:邮件数据在物理存储中的位置,无外乎 数据库中,文件系统中 “挂载位置”:邮件属于哪个逻辑存储位置,例如某某用户的某某文件夹下,某某分类或者聚合的某某路径下。 你乐意的话,讲讲你的 GET POST 怎么完成上面的业务概念。 我已经证明过 RESTful 无论应对哪种需求的展开都有完美的办法。 |
192
Balthild 2017-03-10 14:58:02 +08:00 via Android
@noli
1. 不需要,由于单个动作所具有的原子性,遇到未经过业务逻辑的响应直接丢弃并停止即可。 2. 我说的自然对应法则指的是无例外的、一一对应的法则。 如果只移动逻辑位置而不移动物理位置,必定会造成法则中存在例外情况,而例外情况的存储、查询又要单独处理。 如果直接移动物理位置,就不会在一套对应法则中存在例外。 |
193
noli OP @Balthild
1. 不说逻辑处理的细节都是在扯淡。如果业务协议都是 GET POST 200 ,要怎么写代码才能知道是未经业务逻辑响应? 难道不需要尝试解析一下报文吗。 一句话轻轻带过,你也就是用用框架写写业务代码的水平。 讨论 REST 太为难你了。 2. 不说人话,我也不想和你讨论了。 请你离开此贴。 |
194
Balthild 2017-03-26 14:54:21 +08:00 via Android
@noli
1. 难不成用 REST 就不需要解析?不用 rest 我可以判断完 status code 直接走解析流程,数据都是预定义的、结构化的。用 REST 除了区分 status code ,还要搞一堆你说的乱七八糟的东西,完全是多此一举。 我不仅写后端的业务,我还同时要写前端的。在什么场景下的前后端之间怎么进行数据的交互最方便,恐怕只写后端的人是没有这方面的实践的。 和你讨论 REST 相当为难我,因为我确是在和一个凡事必 REST 而不考虑实际情况的人谈。 2. 你可以选择 Block ,我倍感荣幸。 |
195
noli OP @Balthild
1. 请你先搞清楚,我说的 “报文”不包括 HTTP 头。 GET POST 200 status code 就是得必须解析报文。 正确使用 HTTP STATUS code 例如 REST 的做法,就肯定可以不解析报文。 写过前后端,也就是搞搞 界面搞搞 业务协议的程度吧,这一点都没有改变你对 HTTP 的无知。 写过 怎么从 stream 开始解析 HTTP 请求的代码吗? 如果你真的写过就知道你们这些所谓的 200 党是多么弱智。 你倒是说说什么 HTTP 上的应用 REST 是解决不了的啊,光喊靠这种那种卑鄙暗示对手愚蠢,有用吗? 承认自己经验少不就可以了嘛,非得死撑。 |
196
Balthild 2017-04-19 09:21:04 +08:00 via Android
|
197
noli OP @Balthild 反例一个就够这种话,也就你们这种学生在经验中只发现一个影响因子才会这么推论的吧。说到底还是放弃了用脑子实事求是地分析,依靠本来就少的经验来做经验主义推导。跟你浪费了这么多的时间,都没让你发现你其实自己其实是个浪费别人时间的人,是我的说话方式不够振聋发聩。
这种事情自己实践一遍就知道了。实在是懒,那就用英文搜一下别人怎么看的。就一个简单的协议封装与信息跨层提示问题,拿例子掰开揉碎来讲都不懂。只能说机缘未到。 |
198
Balthild 2017-04-19 10:51:42 +08:00 via Android
@noli
请复习高中数学必修三逻辑学入门,全称命题的否定是一个存在命题,即找出至少一个反例就足够否定全称命题。 说到底 REST 本身不过是 HTTP 上抽象出来的某种模型,这种模型的描述能力本身就只能靠经验来支持。而你又找不出某种类似于对编程语言之图灵完备一样的理论性标准来证明其描述能力,因此反例对一种模型在特定场景的描述性的否定是完全没问题的。 不同人设计出的模型是有优劣,我不否认。然而什么时候总体上有优势的模型必定在任意场有优势了?大学里辩证唯物主义都白学了? |
199
noli OP @Balthild 建模错误。
我不学辩证唯物主义,我大学不考马列毛邓,谢谢。 而且你的辩证唯物主义恰恰犯了常见唯物主义者的错误,就是滥用或者随意扩张词语的意涵。 什么叫做“总体上有优势”的模型,什么叫“任意场”? 讨论 REST 的时候的时候指代什么? |