restful
我不喜欢RESTful原理和API。 近年来,它被视为进程间通信的通用协议,尤其是在分布式系统中。 但是,我看到REST的许多缺陷,对于某些用例,还有一些替代方法可以很好地发挥作用。 显然,没有一个适合所有人的大小 ,我只想强调一下REST体系结构在许多方面都有缺陷。
膨胀的,人类可读的格式,需要额外的压缩
REST的事实上的标准格式是JSON。 至少在XML和信封方面,它比SOAP更好。 要么浪费大量的网络带宽(可能是移动设备或大型数据中心的问题),要么将CPU花费在压缩和解压缩上。 一个可爱的报价:
[该]互联网在调试模式下运行的源
这是一个严重的问题,请问高频交易者如何解析基于文本的FIX有多困难。 有很多行之有效的二进制协议,它们既易于解析又占用很少的内存,例如Protocol buffers , Avro或Thrift 。 此外,它们内置了向后兼容性。 从技术上讲,您可以将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下可用,因此可以轻松访问。 想象一下一个发布域事件,任意状态更改的系统,例如LoanApproved , EmailSent等。当然,每个事件都有其自己独特的属性集。 您如何设计使用此类事件的RESTful API? 不可避免地,您将最终得到POST /domainEvents ,它接受任意JSON,也许带有诸如"type": "LoanApproved"标记。 因此,您有一个采用任意JSON“ BLOB”的终结点,并且很可能具有类似于switch的巨型语句,可以正确解释各种类型的事件。 用"method"替换"type" ,您刚刚重新发明了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
restful