RESTful被认为是有害的

我不喜欢RESTful原理和API。 近年来,它被视为进程间通信的通用协议,尤其是在分布式系统中。 但是,我看到REST的许多不足,对于某些用例,还有一些替代方法可以很好地发挥作用。 显然,没有一个适合所有人的大小 ,我只想强调一下REST体系结构在许多方面都有缺陷。

膨胀的,人类可读的格式,需要额外的压缩

REST的事实上的标准格式是JSON。 至少在XML和信封方面,它比SOAP更好。 要么浪费大量网络带宽(可能是移动设备或大型数据中心的问题),要么将CPU花费在压缩和解压缩上。 一个可爱的报价:

[该]互联网在调试模式下运行的源

这是一个严重的问题,请问高频交易者如何解析基于文本的FIX有多困难。 有很多行之有效的二进制协议,它们既易于解析又占用很少的内存,例如Protocol buffersAvroThrift 。 此外,它们内置了向后兼容性。 从技术上讲,您可以将Google协议缓冲区与基于Spring MVC的REST服务结合使用 -但很少有人以这种方式这样做并坚持使用JSON。

当然,JSON具有一个(字面上是一个 )巨大的优势-它是人类可读的。 好吧,至少只要印刷精美即可 -很少有这种情况。 但是,我们是否真的要为此付出代价,而不是默认情况下仅使用二进制格式,并在需要人工干预时使用翻译器或切换器?

模式和合同都没有

我属于静态打字营。 当我的编译器甚至在运行单元测试之前发现错误时,我就喜欢它-而且我不需要一开始就编写很多错误。 此外,尤其是在函数式编程中,如果代码可以编译,则它可能会起作用,因为按类型强制执行的契约是如此严格。 在处理XML时,我很高兴对XSD进行验证,以便可以确保我阅读的内容符合合同规定。 我可以减少断言的数量并使代码更短。 同样,我产生的XML也保证在语法上是正确的。 最后但并非最不重要的一点是,在使用关系数据库时,严格的架构可以防止我通过插入不正确的值来破坏数据。 与XML类似,我也可以相信自己读到的内容:外键正确,NOT NULL列实际上不为空,等等。这些显然是计算机强制合同的优点。 在设计由分布式系统中的机器(以及偶然地在许多无模式的NoSQL数据库(如MongoDB)中)生产和使用的API时,所有这些都不是很重要。

SOAP的可理解的仇恨将我们推向了另一个极端-根本没有合同。 没有广泛的标准来记录合同并为REST API自动执行合同。 我最近发现的大多数API只是示例请求和响应的集合,充其量是Swagger 。 我不知道哪些字段是可选的,格式和约束是什么。 有人可能会说这是设计使然,我们不会将自己耦合到一个具体的API,而是会不断发展。 但是我有一种感觉,尽管如此,大多数API使用者都是耦合在一起的,一旦推出了未记录和未声明的更改,它们就会崩溃。

发布URI

谈论文档时,纯粹主义者声称API应当公开的唯一信息就是根URI,例如www.example.com/api 。 其他所有内容,包括允许的方法,资源,内容类型和文档,都应通过HATEOAS发现。 而且,如果未正式记录URI而是应发现URI,则意味着我们不应在代码中依赖硬编码的URI,而应在每次使用此类API时遍历资源树。 这是惯用的,但是也很慢且容易出错。 更不用说Swagger,这是REST文档的事实上的标准,正式宣称这种方法不是按设计 ”, 而是坚持使用固定的URI。

不支持批处理,分页,排序/搜索...

RESTful Web服务本身不支持API的许多企业级功能,例如批处理请求,分页,排序,搜索等。 有一些竞争性的建议,例如查询参数,请求标头等。我记得我们花了一个小时的时间讨论有关灵活搜索API的问题。 应该有:

  • 单个类似SQL的参数(带有正确的转义!),例如query=age>10 and (name=John or company=Foo)
  • 每个条件有多个参数,例如age=10&name=John&company=Foo (但是如何实现OR运算符?)
  • 最奇怪的是: /searches上的状态POST具有使用类似JSON的结构建模的条件,将URL返回到搜索结果( /searches/25 ),以后可以查询

REST确实限制了这里。

定义为CRUD

RESTful Web服务是面向CRUD的,而不是面向业务或事务的。 无数次,我们不得不仔细地将业务术语映射到简单的创建 / 更新 / 删除操作中。 世界不是那么简单,并不是所有事物都可以用创建或更新语句来简单描述。 即使可以,RESTful端点通常也很笨拙且人为。 您还记得POST到/searches吗? 而且,并非所有数据都可以映射到URI树结构中,并且我们经常允许非规范化,例如,在多个URI下可以使用相同的资源,以便可以轻松访问它。 想象一下一个发布域事件,任意状态更改的系统,例如LoanApprovedEmailSent等。当然,每个事件都有其自己独特的属性集。 您如何设计使用此类事件的R​​ESTful API? 不可避免地,您将以POST /domainEvents ,该POST /domainEvents可以接受任意JSON,也许带有诸如"type": "LoanApproved"标记。 因此,您有一个采用任意JSON“ BLOB”的终结点,并且很可能具有类似于switch的巨型语句,可以正确解释各种类型的事件。 将"type"替换为"method" ,您刚刚重新发明了JSON RPC 。 您将如何以惯用的方式做到这一点? 将发布者方面的每个域事件转换为适当的API调用? 听起来不是很健壮。

HTTP动词的描述性不足

从业务术语到POST / PUT / PATCH / DELETE / HEAD的映射既繁琐又幼稚。 就像URI一样,世界变得更加丰富,并非所有事物都适合这些存储桶。 这些动词旨在与万维网中的文档和表格进行交互。 使用REST就像将我们的业务领域降级到数据库浏览器一样。 没有逻辑,没有域驱动的设计,没有丰富的流程。 我们只是操纵对象,来回移动它们。 当然,REST不必也不应直接映射到数据库实体。 但是,即使它们映射到您的核心业务域,您仍然必须通过有限的CRUD界面查看域。

将HTTP状态代码与业务答复混合在一起

通过REST发出业务错误的信号通常应使用4xx类状态码。 但是,这些错误并非旨在指示业务案例,因此我不断发现自己试图将4xx代码映射到业务结果。 验证码测试失败? 让我们将其设为400 错误请求 。 表单验证错误? 417 期望失败了 ? 是否因重复而违反约束? 409 冲突 ? 但是乐观锁异常呢? 如果客户的余额不足怎么办? 如果……该怎么办?这些错误代码是为文档检索而设计的,而不是后端的丰富业务。 最后,您将所有内容置于相同的状态代码下或构建复杂的翻译文档。 我们发明了异常,错误Either<T> –只是将自己突然限制在固定的数字错误代码集上。

404特别成问题,因为它是如此普遍。 使用RESTful API时,您永远无法判断404是业务情况还是URL中的错字。 听起来像是分布式调试地狱的秘诀。 哦,我有没有提到没有编码错误的标准方法?

时间耦合

RESTful API确实在微服务爱好者中很流行。 但是,让我们回到基础。 RESTful API基于HTTP,HTTP是建立在TCP / IP之上的请求-响应协议。 TCP / IP在面向数据包的IP协议之上构建连接抽象。 IP几乎无法将消息从一台计算机传递到另一台计算机。 通过构建所有这些抽象级别,我们忘记了Web确实是异步的。 我们相信,通过使用无契约的松散JSON,我们不再将系统耦合在一起。 但是耦合还有另一个维度:时间依赖性。 如果一个系统需要通知另一个事件,那么它们是否确实确实需要同时存在。 在某些情况下(通常使用GET实现),请求-响应很有意义。 但是,在大多数情况下,我们真正想要的是一劳永逸,至少一次的语义。 消息驱动的分布式体系结构被证明更加健壮和容错。 两个系统不再需要看到彼此并同时生活。 而且,如果一个系统产生过多的请求,它将不再破坏另一个系统。 只需在两个系统之间放置一个持久队列。 这种方法具有许多优点:

  • 生产者和消费者可以随时重新启动,而不会丢失数据或降低服务质量
  • 可扩展性更容易实现,无需复杂的负载平衡
  • 我们可以一次将消息发送到多个系统
  • 抽头模式 可能更容易调试

RESTful也没有单向请求的概念。 没有主体的POST越接近越好。 这是有问题的,因为许多业务案例自然都符合“一劳永逸”的语义。 当我发布域事件时,我不在乎响应,但是REST服务与HTTP协议紧密耦合。 但是,仅典型的请求-响应交互(例如通过ID检索用户)并不适合队列。 具有相关性ID的时间队列很尴尬,并引入了大量延迟。

没有标准

没有标准,只有好的,有时是相互矛盾的做法。 我们甚至无法同意资源是单数还是复数,更不用说分页参数,错误处理,HATEOAS了。根据Richardson Maturity Model ,您会发现有人在争论您的服务是RESTful的。 缺乏标准意味着每个人都可以将其服务命名为RESTful。 这可能是Roy Fielding论文的现场示例,也可能是<form> POST处理程序–只要他们使用HTTP,它们就是REST。 这也意味着您现在永远不会如何与任何API进行正确交互。 您应使用哪些标头,如何解码响应,如何协商内容类型(标头?URL中的扩展名?)以及支持哪些类型。

向后兼容

RESTful服务具有处理向后兼容性的几种有缺陷的方式:

  • 仅添加字段,从不删除或更改。 我目睹了一种情况,有人在一个碰巧被直接编码为JSON的对象中修复了一个无辜的错字。 这使另一个系统崩溃
  • 具有版本控制的内容类型–麻烦,需要维护多个版本
  • 如果资源已重命名,则HTTP重定向–仅在客户端足够RESTful的情况下才起作用,从而完全避免了硬编码的URL

上面的每种技术都有其自身的问题,RESTful服务根本不是旨在发展的。

备择方案

在通过HTTP又称为REST嬉皮风格的JSON跳转到JSON之前,我希望您问自己几个问题:

  • 性能是一个问题吗? 然后选择更紧凑的格式,不需要昂贵的压缩
  • 您真的需要阻止请求-响应吗? 如果没有,请考虑消息队列或存储,例如Kafka
  • 您是在进行连续部署还是在自动扩展? 然后选择不临时耦合客户端和服务器且无连接的技术,请参见上文
  • 您的交互是否复杂,或者您正在从模块中提取现有的API /接口到分布式服务? 然后选择更接近经典RPC的技术
  • 您是否需要一次精确的语义 ? 如果是这样,对您没有任何帮助,对不起

PS:强制性附录: “被认为有害”的论文被认为有害

翻译自: https://www.javacodegeeks.com/2015/07/restful-considered-harmful.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值