2KB项目,专业的源码交易网站 帮助 收藏 每日签到

REST 将会是新的 SOAP

  • 时间:2019-01-23 18:23 编辑:2KB 来源:2KB.COM 阅读:458
  • 扫一扫,手机访问
  • 分享
摘要: 英文原文:RES
英文原文:REST is the new SOAP

简介

多年前,我所在的一家大型电信公司开发了一个新型信息系统。我们必须通过旧系统或是友商与越来越多的 web 服务进行通讯。

更不用说,我们合理的拥有 SOAP Hell 的份额,玄奥的 WSDL ,不相容的 library ,奇怪的 bug ...所以只要可以,我们就提倡使用简单的远程过程调用协议:XMLRPC 或 JSONRPC 。

我们为这些协议提供的首批服务器与客户端非常基础,单调,脆弱。 但渐渐的,我们改善了它们; 通过几百行额外的代码,我们让所想变成现实:支持不同的方言(例如 Apache 特定的 XMLRPC 扩展),python 异常和分层错误代码之间的内置转换,功能和技术错误的单独处理,后续的自动重审,请求之前或之后的相关日志记录和统计信息,输入数据的彻底验证......

现在,我们只需要几行代码,就能和这样的API建立可靠的连接。

我们也只需要稍微修饰一下,做一些文档更新,就可以暴露一套新的功能给广泛的受众、服务器或者web浏览器。

对于应用间的通信(微服务风格),系统管理员自己就可以完成这些工作;对于软件层面,这几乎是透明的。

用了30分钟集成RPC API后,程序员在休息。

然后,REST出现了。 
表述性状态转移(Representational State Transfer)。

一股复兴浪潮动摇了跨服务通信的根基。

RPC已死,未来是RESTful的:每个资源都有自己的URL,并通过HTTP协议进行操作。

然后,我们必须暴露或调用的API,成为了新的挑战;这简直愚蠢至极。

REST有什么问题呢?

一个简短的例子就值得长篇大论。下面是一个小API,为了可读性删除了数据类型。

createAccount(username, contact_email, password) -> account_id

addSubscription(account_id, subscription_type) -> subscription_id

sendActivationReminderEmail(account_id) -> null

cancelSubscription(subscription_id, reason, immediate=True) -> null

getAccountDetails(account_id) -> {full data tree}

仅添加一个合适文档化的异常层次结构 (InvalidParameterError, MissingParameterError, WorkflowError…),使子类可识别重要的用例(例如AlreadyExistingUsernameError),这样你就可以了。

这些API易于理解、易于使用,并且是健壮的。他们是有精准的状态机支持,但有限的可用操作集使得用户远离无意义的交互(例如修改账户的创建日期)。 

其它翻译版本 (1) 加载中

将这个 API 暴露为一个简单的 RPC 服务,估计用时:几个小时。

现在,试试 RESTful。

没有太多的标准和规范,只有一个模糊的“RESTful哲学”,所以容易引起无休止的、形而上学的争论,还催生了许多不优雅的变通方案。

如何将上面明确的功能,映射为简单的 CRUD 操作?发送验证邮件,是更新一下"must_send_activation_reminder_email"属性,还是创建一个"activation_reminder_email resource"资源?如果在宽限期内,订阅仍然有效,或者可能恢复订阅,那用 DELETE 操作执行 cancelSubscription() 是否合理?如何在节点间拆分 getAccountDetails() 的数据树,来使它符合 REST 模型?

为每个资源分配什么 URL?是不难,但也需要实现。

如何使用非常有限的 HTTP 响应码,来表达错误场景的差别?

使用什么序列化、什么格式来描述输入输出?

HTTP 方法、URL、查询参数、负载、请求头、响应码,它们的分界线在哪?

花费了很多时间重复造轮子,甚至造的并不是好轮子。一个不完整的、易碎的轮子,需要通过大量文档来理解它,甚至不知不觉就违反了规范。

为什么 REST 带来了这么多工作(Work)?
这是一个悖论,也是一个双关(译者注:REST 在英文中有休息的意思)。

让我们深入探讨一下这个设计哲学所产生的人为问题。

有趣的 REST

REST 不是 CRUD,它的拥护者们不会让你混淆这两者。然而不久,他们会为 HTTP 已经提供了 CRUD 的语义而欣喜,例如创建(POST)、获取(GET)、更新(PUT/PATCH)和删除(DELETE)。

他们乐于承认这几个动词足以表达任何操作。嗯,当然是这样;就像用几个动词足以表达英语中的任何概念一样:“Today I updated my CarDriverSeat with my body, and created an EngineIgnition, but the FuelTank deleted itself(今天我用我的身体更新了我的汽车座椅,并且创建了一次发动机点火,但是油箱删除了它自己)”;这是不是有点尴尬。除非你是道本语的崇拜者。

追求极简是好事,但起码要做好。你知道为什么从来不在 Web 表单上使用 PUT、PATCH 和 DELETE 吗?因为它们百害而无一利。我们只需要用 GET 来读,用 POST 来写就好了。或者,当我们不需要 HTTP 层缓存时,只用 POST 就好了。其他方法好点的话也许会妨碍你,但最差的情况下会毁了你的一天。

想用 PUT 更新资源?可以,但是一些神圣的规范要求,数据输入必须与 GET 读取到的数据描述一致。那么,如何处理 GET 返回的大量只读参数呢(创建时间、最后更新时间、服务器生成的令牌……)?你准备忽略它们,而违反 PUT 使用原则吗?还是考虑它们,如果它们不符合服务端的值时,抛出"HTTP 409 Conflict"异常(强迫你再调用一次 GET……)?还是你给它们随机值,并祈祷服务端会忽略它们(沉浸在不会报错的喜悦中)?选个死法吧,REST 显然不知道什么是只读属性, 短期内也不会解决这件事。此外,用 GET 来返回密码信息(或信用卡号码)是危险的,之前都使用 POST 或 PUT;处理这些只写参数时,也只能祝君好运了。

我是不是忘了提 PUT 有可能会造成竞态条件,不同的客户端会覆盖其他客户端的变更,尽管它们只是想更新不同的字段。

又想用 PATCH 来更新资源?很好,但是,就像 99% 的人使用这个动词一样,只需要在请求负载中传递资源字段的子集,然后希望服务器能够正确地理解操作意图(和所有副作用);许多资源参数或是紧密联系,又或是相互排斥的(例如:在用户账单信息中,要么是信用卡号,要么是 PayPal 令牌),但是 RESTful 设计原则对这些重要信息避而不谈。不管怎么说,你会再次违反原则:PATCH 不应该只发送一堆需要被更新的参数。相反,应该提供给服务端一些指示信息来应用到资源上(译者注:不应该发一堆参数让服务端来猜,需要给服务一些提示,让服务器理解你的操作意图)。又到你了,拿上你的纸板和咖啡杯,你必须决定如何来表达这些指示信息。通常需要自定义规范,因为没有标准就是 REST 世界里的事实标准。(编辑注:REST 倡导者在这个问题上有所让步,提出了 Json Merge Patch,这是一个 Json Patch 的候选方案)

想用 DELETE 删除资源?好,但我希望你,不需要提供大量的上下文数据;就像用户对 PDF 扫描的终止请求。DELETE 不允许包含有效负载。这一点,REST 架构师经常忽略掉,因为大多数 Web 服务器不会对收到的请求强制执行这个规则。如果一个 DELETE 请求携带一个 2MB 的 Base64 字符串,怎么兼容规范呢?(编辑注:RFC 2616 指明了没有语义的负载应该被忽略,但现在已经废弃了)

REST 爱好者通常信奉“人们都做错了”,他们的 API“并不是真正的 RESTful”的。例如,许多开发者使用 PUT 直接在最终 URL 上创建资源(/myresourcebase/myresourceid), 然而,“正确的用法”(编辑注:根据很多人)应该是,在上一层URL(/myresourcebase)使用 POST 来创建资源,然后服务器通过 HTTP 的"Location"头来指明新资源的 URL(编辑注:不过,这不是 HTTP 重定向)。好消息是:这没关系。这些严格的准则就像是高位优先 vs 低位优先,它们让哲学家们费劲脑汁,但对现实没有什么影响,换言之,把事情做好就可以了。

顺便提一下……设计 URL 很有意思。你知道在构建 REST URL 时,有多少 urlencode() 的正确实现吗?如果没有,就等着接受 SSRF/CSRF 攻击吧。


当你忘记在 30 个 URL 中的其中一个对用户名进行 urlencode 时……

REST错误处理中的乐趣

每一个编码者都可以使“名义上的用例”工作。错误处理就是这些功能里的一种,它将决定你的代码是否是健壮软件,还是一大推火柴棍。

HTTP提供了一系列开箱即用的错误码列表。好极了,让我们了解下。

使用“HTTP 404 Not Found”来通知那些不存在的资源,看起来很符合REST风格,难道不是嘛?太糟糕了:你的nginx被错误配置了一个小时,所以你的API消费者仅获得了404错误,并清除了数百个账户,他们认为这些账户已经被删除了... 

我们的客户,在我们因为错误而删除其数G字节的重要镜像之后。

其它翻译版本 (1) 加载中

当用户对一个第三方服务没有访问权限时,使用"HTTP 401 Unauthorized",这听起来是可以接受的,不是吗?但是,如果你在 Safari 浏览器的 Ajax 调用中获得此错误码,它会弹出一个密码输入框,这会让你的客户很吃惊[几年前,确实是这样的,你可能有不同意见]。

HTTP 比 RESTful 历史长得多,Web 生态系统充满了关于错误码含义约定俗成的东西。用 HTTP 状态码来表示应用错误,就像是用牛奶瓶装剧毒废物一样:总有一天会遇到麻烦。

一些标准的 HTTP 错误码是 WebDAV 专用的,另一些是微软专用的,剩下的有一些定义模糊,没有太大用处。最后,和大多数 REST 用户一样,你可能开始随意使用 HTTP 状态码,比如“HTTP 418 I’m a teapot”或未分配的数字,以用来表达应用程序中的特定异常。或者,厚着脸皮用“HTTP 400 Bad Request”来表示所有功能错误,然后用布尔、整型代码、slug 和翻译信息整合成笨重的错误格式,填充到负载中。或者,完全放弃正确的错误处理;只返回一个用自然语言描述的普通消息,并希望调用者能够分析问题,并采取行动。当与自治系统的这些 API 交互时,只能祝君好运了。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部