REST是新的SOAP

壤驷华美
2023-12-01

本文翻译自下面的链接。

https://medium.freecodecamp.org/rest-is-the-new-soap-97ff6c09896d

REST is the new SOAP

REST是新的SOAP(Simple Object Access Protocol)

Written by Pascal Chambon, reviewed by Rapha?l Gomès

作者Pascal Chambon,审校Rapha?l Gomès

Update: this article mostly deals with the RESTish ecosystem, which now constitutes a major part of webservices. For more in-depth analysis of the original REST, and of HATEOAS, see my follow-up article.

更新:本文主要涉及现在构成网络服务大多数部分的像REST一样的生态系统。想知道更多深入的原始的REST的分析还有HATEOAS,参考我的本文的后续文章。

Introduction

介绍

Some years ago, I developed a new information system in a big telecom company. We had to communicate with an increasing number of web services, exposed by older systems or by business partners.

一些年之前,我在一个大公司开发了一个新的信息系统。我们不得不和不断增加数量的网络服务通信。这些服务或者是由旧的系统或者是由新的商业伙伴暴露的。

Needless to say, we had our fair share of SOAP Hell. Abstruse WSDLs, incompatible libraries, weird bugs… So whenever we could, we advocated?—?and used?—?simple Remote Procedure Call protocols: XMLRPC or JSONRPC.

不用说,我们有大量的SOAP陷阱、深奥难懂的WSDL(Web Service Description Language),不兼容的库,怪异的缺陷…)。所以我们无时不抓住机会倡导并且自己使用简单的RPC(远程方法调用协议)协议:XMLRPC或JSONRPC(XML: Extensible Markup Language, JSON: Java Syntax Object Notation).

Our first servers and clients for these protocols were pretty basic, limited, fragile. But gradually, we improved them; and with a few hundreds lines of additional code, we achieved the dream: support for different dialects (such as Apache-specific XMLRPC extensions), built-in conversion between python exceptions and hierarchical error codes, separate handling of functional and technical errors, auto-retries for the latter, relevant logging and stats before/after requests, thorough validation of input data…

我们初期的使用这些协议的服务器和客户端非常基础、局限和脆弱。但是渐渐地,我们提高了他们;用几百行额外的代码,我们实现了梦想:支持不同的方言(比如Apache特定的XMLRPC的扩展),内建的Python异常和层级的错误码的转换,分开处理功能的和技术的错误,对技术的错误的重试,记录请求之前和之后的相应的日志和统计,彻底的对输入数据的验证…

Now we were able to robustly connect to any such API, with just a few lines of code.

现在我们可以健壮地使用很少行数的代码连接任意这样的API。

Now we were able to expose any set of functions to a wide audience, to servers and to web browsers, with a few decorators and doc updates.

现在我们可以使用很少的修饰和文档的更新来暴露任意集合的功能给一大波听众、服务器和浏览器。

And when it came to communicating between our different applications (microservice-style), it was a job for our system administrator; software-side, it was almost transparent.

谈到我们不同的应用之间的交流(微服务风格),过去它是系统管理员的任务;软件层面,这几乎是透明的。

[picture:Developer resting after a tough 30mn spent integrating an RPC API.]

[图片:开发者在经过了30分钟的艰苦的集成RPC API(Remote Procedure Call Application Programming Interface)工作后在休息]

Then came REST.

然后REST来了。

REpresentational State Transfer.

表现状态转移

A wave of renewal shook the foundations of inter-services communication.
RPC was dead, the future was RESTful: resources living each on its own URL, and manipulated exclusively through HTTP protocol.

一波革新动摇了跨服务交流的根基。RPC死了,未来是REST的:资源存在于它自身的URL(Unified Resource Link)上,由HTTP协议来操作。

From then on, every API we had to expose or consume became a new challenge; not to say a testimony to insanity.

从那开始,每一个我们必须暴露或消费的API都成为一个新挑战;不用说荒唐的证明(不是很确切)。

What’s the problem with REST?

REST有什么问题?

A short example is worth a long talk. Here is a small API, with data types removed for readability.

短的例子胜过长篇大论。这是一个小的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}

Just add a properly documented hierarchy of exceptions (InvalidParameterError, MissingParameterError, WorkflowError…), with subclasses to identify important cases (eg. AlreadyExistingUsernameError), and you’re good to go.

仅需要加上很好地描述了的分层级的异常(InvalidParameterError, MissingParameterError, WorkflowError…),和用子类来标识重要的场景(比如AlreadyExistingUsernameError),这就足够好了。

This API is easy to understand, easy to use, and robust. It is backed by a precise state machine, but the restricted set of available operations keeps users away from nonsensical interactions (like changing the creation date of an Account).

这个API易于理解、容易使用并且健壮。它背后由精确的状态机支撑,受限的可用的系列操作使用户避免做傻事(比如修改账户的创建日期)。

Estimated time to expose this API as a simple RPC service: a few hours.

估计暴露这个API为简单的RPC的时间:几小时。

Ok, now time to go the RESTful way.

现在采用REST方式。

No more standards, no more precise specifications. Just a vague “RESTful philosophy”, prone to endless metaphysical debates, and as many ugly workarounds.

没有标准,没有精确的规范。只有一个模糊的”REST哲学”,容易导致无穷的形而上的争论,和许多的丑陋的变通方案。

How do you map the precise functions above, to a handful of CRUD operations? Is sending the activation reminder email an update on a “must_send_activation_reminder_email” attribute? Or the creation of a “activation_reminder_email resource”? Is it sensible to use DELETE for cancelSubscription() if the subscription remains alive during a grace period, and may be resurrected during that time? How do you split the data tree of getAccountDetails() between endpoints, to respect the data model of REST?

你如何把如上的精确的函数映射为方便的CRUD(create, retrieval, update, delete)操作?”激活提醒邮件”,还是创建”激活提醒邮件资源”是对”必须激活提醒邮件”属性的更新?如果订阅在临界死亡期间,并且期间可能复活,用DELETE(删除)操作来取消订阅合理吗?你如何在节点间拆分getAccountDetails()函数的数据树来使它符合REST模型?

What URL endpoint do you assign to each of your “resources”? Yeah it’s easy, but it has to be done anyway.

你给你的每一个资源分配什么URL节点?是的这简单,但是它必须要做。

How do you express the diversity of error conditions, using the very limited bunch of HTTP codes?

你如何用有限的HTTP的返回值表达错误场景的差别?

What serialization formats, which specific dialects do you use for input and output payloads?

你使用哪种序列化格式,哪种方言来描述输入和输出的负载?

How exactly do you scatter these simple signatures between HTTP method, URL, query string, payload, headers, and status code?

你如何分解这些简单的函数、参数和返回值为HTTP方法、链接、查询参数、负载、消息头和状态返回值?

And you’re gone for hours, reinventing the wheel. Not even a tailored, smart wheel. A broken and fragile wheel, requiring tons of documentation to be understood, and violating specifications without even knowing it.

你会花数小时重复发明轮子。而且都不是裁剪了的聪明的轮子。一个破了的、易碎的轮子,需要大量文档来理解它,即使违反了规范都不知道。

[picture 2]

[第二张图]

How come REST means so much WORK?

为啥REST意味着这么多工作?

This is both a paradox, and a shameless pun.

这既是一个矛盾,又是一个无耻的双关(REST也指休息的意思)。

Let’s dive further into the artificial problems born from this design philosophy.

让我们一起来挖一下这个设计哲学导致的人为的问题。

The joy of REST verbs

REST动词的好笑

Rest is not CRUD, its advocates will ensure that you don’t mix up these two. Yet minutes later they will rejoice that HTTP methods have well defined semantics to create (POST), retrieve (GET), update (PUT/PATCH) and delete (DELETE) resources.

REST不是CRUD(查插删改),它倡导你不要混淆这俩词。然而几分钟后,他们会庆祝HTTP方法具有正确的create(创建,POST), retrieve(获取Get), update(更新PUT/PATCH)和delete(删除DELETE)资源的语义。

They’ll delight in professing that these few “verbs”are enough to express any operation. Well, of course they are; the same way that a handful of verbs would be enough to express any concept in English: “Today I updated my CarDriverSeat with my body, and created an EngineIgnition, but the FuelTank deleted itself”; being possible doesn’t make it any less awkward. Unless you’re an admirator of the Toki Pona language.

他们欣喜于承认这些动词足够表达任何操作。好吧,当然他们可以;就像几个动词可以用英语描述任何概念一样:“今天我用我的身体更新了我的车的驾驶座椅,创建了一次引擎打火,但是油箱删除了它自己”;可以这样做不代表这样做不尴尬,除非你是道本语(一种人造语)的拥泵。

If the point is to be minimalist, at least let it be done right. Do you know why PUT, PATCH, and DELETE have never been implemented in web browser forms? Because they are useless and harmful. We can just use GET for read and POST for write. Or POST exclusively, when HTTP-level caching is unwanted. Other methods will at best get in your way, at worst ruin your day.

如果追求极简,至少要做正确。你知道为什么PUT, PATCH和DELETE操作从来没有被浏览器的窗体实现吗?因为他们无用有害。我们可以用GET来读,用POST来写。或者当我们不想要HTTP层的缓存的时候,我们可以只用POST。其余的操作最好的情况会挡你的路,最坏的情况会毁你的生活。

You want to use PUT to update your resource? OK, but some Holy Specifications state that the data input has to be equivalent to the representation received via a GET. So what do you do with the numerous read-only parameters returned by GET (creation time, last update time, server-generated token…)? You omit them and violate the PUT principles? You include them anyway, and expect an “HTTP 409 Conflict” if they don’t match server-side values (forcing you to then issue a GET…)? You give them random values and expect servers to ignore them (the joy of silent errors)? Pick your poison, REST clearly has no clue what a read-only attribute it, and this won’t be fixed anytime soon. Meanwhile, a GET is dangerously supposed to return the password (or credit card number) which was sent in a previous POST/PUT; good luck dealing with such write-only parameters too.

你想用PUT操作来更新你的资源吗?好吧,但是某些神圣的规范说数据输入必须和通过GET获得的表示等价。所以,你拿这些大量的只读的由GET返回的参数(创建时间、上次更新时间、服务器生成的令牌)做什么?你忽略他们来违反PUT的原则吗?还是你无论如何包括他们,然后如果他们不匹配服务端的值(这时强迫你调用一次GET),你期待”HTTP 409冲突”?还是你给他们随机值然后期待服务器忽略他们(无声的错误就当作不存在)?你自己挑选你的死法吧,REST肯定没有迹象指明什么是只读属性,这短期内不会被解决的。同时GET返回之前PUT或POST发送的密码(或信用卡号)信息是危险的;祝你在处理这些只写的参数时好运。

Did I forget to mention that PUT also brings dangerous race conditions, where several clients will override each other’s changes, whereas they just wanted to update different fields?

我忘了提PUT也会带来危险的竞争状态了吗?当几个客户端仅想更新各自不同的域的时候,会覆盖其他人的改动。

You want to use PATCH to update your resource? Nice, but like 99% of people using this verb, you’ll just send a subset of resource fields in your request payload, hoping that the server properly understands the operation intended (and all its possible side effects); lots of resource parameters are deeply linked or mutually exclusive(ex. it’s either credit card OR paypal token, in a user’s billing info), but RESTful design hides this important information too. Anyway, you’d violate specs once more: PATCH is not supposed to just send a bunch of fields to be overridden. Instead, you’re supposed to provide a “set of instructions” to be applied on the resources. So here you go again, take your paperboard and your coffee mug, you’ll have to decide how to express these instructions. Often with handcrafted specifications, since Not-Invented-Here Syndrome is a de-facto standard in the REST world. (Edit: REST advocates have backpedaled on this subject, with Json Merge Patch, an alternative to formats like Json Patch)

你想用PATCH来更新你的资源吗?很好,如99%的使用这个操作的人一样,你会在你的负载里发送一个子集的资源域然后希望服务器理解这个操作的意图(和所有可能的副作用);许多资源参数或者是深度关联的,或者是互不相关的(例如在用户账单信息里,或者用银行卡或者用支付宝),但是REST设计隐藏了这些重要的信息。不管怎样,你会再一次违反规范:PATCH不是被用来发送一打需要被覆盖的域的,而是你被期待提供一系列的指令来应用到资源上。又需要你了,拿好你的文件夹和咖啡杯,你要决定如何表达这些指令。经常需要手工的规范,因为没有标准是REST世界的事实标准。(编辑:REST的倡导者对这个主题已经变卦了,他们让你使用JSON合并PATCH,另外一种像JSON PATCH的候选方案)

You want to DELETE resources? OK, but I hope you don’t need to provide substantial context data; like a PDF scan of the termination request from the user. DELETE prohibits having a payload. A constraint that REST architects often dismiss, since most webservers don’t enforce this rule on the requests they receive. How compatible, anyway, would be a DELETE request with 2 MBs of base64 query string attached? (Edit: the RFC 2616, indicating that payloads without semantics should be ignored, is now obsolete)

你想删除资源吗?好的,但是我不希望你需要提供基本的上下文数据;比如从用户来的终止PDF文档扫描的请求。删除操作中禁止负载。这点REST架构者经常不承认,因为大多数的网络服务器不会对他们收到的请求强制这条规则。如果一个DELETE操作带有2MB大小的BASE64编码的字符串参数,这是否兼容规范?(编辑:RFC2616指明了没有语义的负载应该被忽略,词RFC已经无效了)

[picture 3]

[图片 3]

REST aficionados easily profess that “people are doing it wrong” and their APIs are “actually not RESTful”. For exemple, lots of developers use PUT to create a resource directly on its final URL (/myresourcebase/myresourceid), whereas the “good way” (edit: according to many) of doing it is to POST on a parent URL (/myresourcebase), and let the server indicate, with an HTTP “Location” header, the new resource’s URL (edit: it’s not an HTTP redirection though). The good news is: it doesn’t matter. These rigorous principles are like Big Endian vs Little Endian, they occupy philosophers for hours, but have very little impact on real life problems, i.e “getting stuff done”.

REST迷经常信奉”人们做错了”,他们的API不是REST的。例如,很多开发者使用PUT操作来直接在它的最终URL上创建资源(/myresourcebase/myresourceid),然而正确的方法(编辑:根据很多人)应该是在上一层URL使用POST操作(/myresourcebase), 然后服务器来通过HTTP的”Location”头来指明新的资源的URL(编辑:这不是HTTP重定向)。好消息是:这没关系。这些严格的原则就像吃鸡蛋从大端还是小端吃一样,哲学家为此费脑筋,但是对生活没影响,关键是把事做成。

By the way… handcrafting URLs is always great fun. Do you know how many implementations properly urlencode() identifiers while building REST urls? Not that many. Get ready for nasty breakages and SSRF/CSRF attacks.

顺便说下,设计URL总是充满乐趣。你知道构造REST URL时有多少合适的urlencode()函数的实现吗?不多。准备好险恶的破坏和SSRF/CSRF攻击吧。

[picture 4]

[图片 4]

When you forget to urlencode usernames in 1 of your 30 handcrafted URLs.

当你忘了编码URL中30个中的1个用户名的时候

The joy of REST error handling

REST错误处理的乐子

About every coder is able to make a “nominal case” work. Error handling is one of these features which will decide if your code is robust software, or a huge pile of matchsticks.

大约每一个程序员都可以使一个名义上的案例工作。错误处理是决定你的代码是健壮的软件还是一堆火柴棍的因素之一。

HTTP provides a list of error codes out-of-the-box. Great, let’s see that.

HTTP开箱提供了错误值的列表。很好,我们来瞧瞧。

Using “HTTP 404 Not Found” to notify about an unexisting resource sounds RESTful as heck, doesn’t it? Too bad: your nginx was misconfigured for 1 hour, so your API consumers got only 404 errors and purged hundreds of accounts, thinking they were deleted….

用”HTTP 404没找到”来通知不存在的资源听起来像鬼一样符合REST,不是吗?太坏了:你的NGINX服务器配置错误一个小时,你的API的用户仅能得到404错误,然后他们已经清洗了上百的账号了,因为他们认为这些用户已经被删除。

[picture 5]

[图片 5】

Our customers, after we deleted their gigabytes of kitten images by error.

在我们错误删除了上GB的小猫图片后我们的客户的反应

Using “HTTP 401 Unauthorized” when a user doesn’t have access credentials to a third-party service sounds acceptable, doesn’t it? However, if an ajax call in your Safari browser gets this error code, it might startle your end customer with a very unexpected password prompt [it did, years ago, YMMV].

用”HTTP 401未授权”来表示一个用户没有访问一个第三方的服务的权限听起来合理,不是吗?然而,如果你的Safari浏览器的一个ajax(Asynchronous JavaScript And XML)调用得到了这个错误,它可能会弹出一个让输入密码的对话框来惊吓你的客户[几年前它这样做过,YMMV(Your Mileage May Vary你的里程表可能不一样,你可能有不同意见)]

HTTP existed long before “RESTful webservices”, and the web ecosystem is filled with assumptions about the meaning of its error codes. Using them to transport application errors is like using milk bottles to dispose of toxic waste: inevitably, one day, there will be trouble.

HTTP在”REST网络服务”前存在很久了,网络生态系统对于它的错误值有预设。用他们表示应用错误就像用牛奶瓶来装有毒废物:有一天,不可避免,麻烦会来。

Some standard HTTP error codes are specific to Webdav, others to Microsoft, and the few remaining have definitions so fuzzy that they are of no help. In the end, like most REST users, you’ll probably use random HTTP codes, like “HTTP 418 I’m a teapot” or unassigned numbers, to express your application-specific exceptions. Or you’ll shamelessly return “HTTP 400 Bad Request” for all functional errors, and then invent your own clunky error format, with booleans, integer codes, slugs, and translated messages stuffed into an arbitrary payload. Or you’ll give up altogether on proper error handling; you’ll just return a plain message, in natural language, and hope that the caller will be a human able to analyze the problem, and take action. Good luck interacting with such APIs from an autonomous program.

一些标准的HTTP的错误值是Webdav特定的,另外一些是微软特定的,另外一些剩余的定义得太模糊没有什么实际帮助。最后,像大多数REST用户,你可能会用随机的HTTP错误码,比如“HTTP 418我是一个茶壶”或者是没有分配的错误值,来表达你的应用相关的异常。或者你无耻地为每个功能错误都返回”HTTP 400坏的请求”错误,然后你用整形、布尔型、slug、翻译的消息等来拼凑你自己的笨重的错误格式,然后把他们放进人为的负载中。或者你完全放弃合适的错误处理;你只是返回一个自然语言描述的普通消息,然后期待调用者是个人类并能够分析问题采取行动。

The joy of REST concepts

REST概念的乐子

REST has made a career out of boasting about concepts that any service architect in his right mind already respects, or about principles that it doesn’t even follow. Here are some excerpts, grabbed from top-ranked webpages.

REST已经靠吹嘘概念赢得了事业,这些概念或者是任何服务架构都已经遵守了的,或者是它自己从不曾遵守的原则。这里是从高权重的网页上得来的一些摘录。

REST is a client-server architecture. The client and the server both have a different set of concerns. What a scoop in the software world.

REST是一种客户端服务器架构。客户端和服务器有不同的关注点。这真是软件世界的独家新闻。

REST provides a uniform interface between components. Well, like any other protocol does, when it’s enforced as the franca lingua of a whole ecosystem of services.

REST在组件之间提供一种统一的接口。好的,当在整个服务的生态系统强迫使用一种通用语的时候,任何其他协议都可以做到这一点,

REST is a layered system. Individual components cannot see beyond the immediate layer with which they are interacting. It sounds like a natural consequence of any well designed, loosely coupled architecture; amazing.
Rest is awesome, because it is STATELESS. Yes there is probably a huge database behind the webservice, but it doesn’t remember the state of the client. Or, well, yes, actually it remember its authentication session, its access permissions… but it’s stateless, nonetheless. Or more precisely, just as stateless as any HTTP-based protocol, like simple RPC mentioned previously.

REST是一个分层的系统。各个组件不能够跨过和他们直接交互的层来彼此看见。这听起来是任何很好设计的松耦合的架构的自然结果;令人吃惊。REST令人敬畏,因为它是无状态的。是的,服务后面可能有一个巨大的数据库,但是它可能不会记住客户端的状态。或者,好吧,是的,事实上它会记住客户端的鉴权的事务,访问的权限等等,但是它是无状态的。或者更精确一点,就像任何基于HTTP的协议一样无状态,就像之前提到的简单的RPC一样。

[picture 6]

[图片 6]

With REST, you can leverage the power of HTTP CACHING! Well here is at last one concluding point: a GET request and its cache-control headers are indeed friendly with web caches. That being said, aren’t local caches (Memcached etc.) enough for 99% of web services? Out-of-control caches are dangerous beasts; how many people want to expose their APIs in clear text, so that a Varnish or a Proxy on the road may keep delivering outdated content, long after a resource has been updated or deleted? Maybe even delivering it “forever”, if a configuration mistake once occurred? A system must be secure by default. I perfectly admit that some heavily loaded systems want to benefit from HTTP caching, but it costs much less to expose a few GET endpoints for heavy read-only interactions, than to switch all operations to REST and its dubious error handling.

通过REST,你可以利用HTTP缓存的功能。好的,这至少是一个结论点:一个GET请求和它的缓存控制头确实是利于HTTP缓存。那么,难道本地的缓存(Memcached等)不是针对99%的网络服务有效吗?失去控制的缓存是危险的野兽;有多少人愿意以文本的形式暴露他们的API呢,这样中间的Varnish缓存或者代理就会持续地分发早就被更新和删除的过期的内容?一个系统必须缺省是安全的。我绝对同意一些高负载的系统需要利用HTTP缓存,但是暴露少量只读的GET交互资源比全部都切换到REST操作和可疑的错误处理成本要低得多。

Thanks to all this, REST has HIGH PERFORMANCE! Are we sure of that? Any API designer knows it: locally, we want fine-grained APIs, to be able to do whatever we want; and remotely, we want coarse-grained APIs, to limit the impact of network round-trips. Here is again a domain in which REST fails miserably. The split of data between “resources”, each instance on its own endpoint, naturally leads to the N+1 Query problem. To get a user’s full data (account, subscriptions, billing information…), you have to issue as many HTTP requests; and you can’t parallelize them, since you don’t know in advance the unique IDs of related resources. This, plus the inability to fetch only part of resource objects, naturally creates nasty bottlenecks.

感谢所有这些,REST具有高性能。我们确定吗?任何API的设计者都知道:在本地,我们想要精细的API来做任何我们想做的;在远程,我们想要粗粒度的API来降低网络来回的影响。这又是REST可悲地失败的领域。数据和资源的分离,每个实例在它自己的端点上,自然地导致N+1查询问题。为了获取用户的全部数据(账户,订阅,账单…),你必须发起很多HTTP请求;你还不能并行化他们,因为你事先不知道相应的资源的唯一的标识。这再加上不能够获取资源对象的一部分,自然地产生险恶的瓶颈。

REST offers better compatibility. How so? Why do so many REST webservices have “/v2/” or “/v3/” in their base URLs then? Backwards and forward compatible APIs are not hard to achieve, with high level languages, as long as simple rules are followed when adding/deprecating parameters. As far as I know, REST doesn’t bring anything new on the subject.

REST提供更好的兼容性。为什么这样呢?因为很多REST网络服务的URL上都有版本2或版本3标识。通过高级语言,只要在添加或废弃参数的时候遵循一些简单的规则,对API的向后和向前兼容不难实现。就我所知,REST在这个主题没有带来新东西。

REST is SIMPLE, everyone knows HTTP! Well, everyone knows pebbles too, yet people are happy to have better blocks when building their house. The same way XML is a meta-language, HTTP is a meta-protocol. To have a real application protocol (like “dialects” are to XML), you’ll need to specify lots of things; and you’ll end up with Yet Another RPC Protocol, as if there were not enough already.

REST是简单的,每个人都知道HTTP。好的,每个人还都知道鹅卵石呢,但是人们建房子的时候喜欢用更好的砖。正如XML是元语言,HTTP是元协议。定义真正的应用层协议(就像XML的方言),你需要指定很多事;你将会得到另一种RPC协议,就像RPC协议还不够多一样。

REST is so easy, it can be queried from any shell, with CURL! OK, actually, every HTTP-based protocol can be queried with CURL. Even SOAP. Issuing a GET is particularly straightforward, for sure, but good luck writing json or xml POST payloads by hand; people usually use fixture files, or, much more handy, full-fledged API clients instantiated directly in the command line interface of their favorite language.

REST很简单,用CURL工具可以在任何终端控制台上查询。好的,事实上每个基于HTTP的协议都可以用CURL来查询。即使是SOAP。发起一个GET请求特别地直接,但是你在通过手写来写JSON或XML的负载的时候,我只有祝你好运了;人们通常使用固定文件,或者更方便地,在命令行用他们喜欢的语言实现全功能的API客户端。

“The client does not need any prior knowledge of the service in order to use it”. This is by far my favourite quote. I’ve found it numerous times, under different forms, especially when the buzzword HATEOAS lurked around; sometimes with some careful (but insufficient) “except” phrases following. Still, I don’t know in which fantasy world these people live, but in this one, a client program is not a colony of ants; it doesn’t browse remote APIs randomly, and then decide how to best handle them, based on pattern recognition or black magic. Quite the opposite; the client has strong expectations on what it means, to PUT this one field to this one URL with this one value, and the server had better respect the semantic which was agreed upon during integration, else all hell might break loose.

“客户不需要任何服务的预备知识就能够使用它。”这是目前我最喜欢的引用了。我发现它很多次了,以不同的形式,尤其是热词HATEOAS(Hypermedia As The Engine Of Application State)出现的时候;有时后面会跟一些小心的(不充分的)例外短语。我仍然不知道这些人居住在什么样的神奇世界,但是这一条,一个客户程序不是一群蚂蚁;它不会随机地浏览远程API然后利用模式识别或者黑魔法来决定怎么最好地使用它们。恰恰相反;客户端对为什么把这个URL中的这个域设为这个值有强烈的期待,服务端最好尊重在集成的时候双方约定的语义,否则所有地狱里的小鬼都可能挣脱。

[picture 7]

[图片 7]

When you ask how HATEOAS is supposed to work.

当你问HATEOAS如何工作的时候。

How to do REST right and quick?

如何快速把REST做正确?

Forget about the “right” part. REST is like a religion, no mere mortal will ever grasp the extent of its genius, nor “do it right”.

忘掉“正确”这一部分。REST像宗教,凡人不能够掌握它的天才程度,也不会把它做对。

So the real question is: if you’re forced to expose or consume webservices in a kinda-RESTful way, how to rush through this job, and switch to more constructive tasks asap?

所以真正的问题是:如果你必须以REST的方式暴露或使用网络服务,如何快速完成工作,然后越快越好地去做更有创造性的工作。

Update: it turns out that there are actually lots of “standards” and industrialization efforts for REST, although I had never encountered them personnally (maybe because few people use them?). More information in myfollow-up article.

更新:结果事实上REST有许多标准和工业化的努力,虽然我个人没有遇到他们(也许因为很少有人使用他们?)更多的信息参见我的本文的后续文章。

How to industrialize server-side exposure?

如何使服务端的暴露工业化?

Each web framework has its own way of defining URL endpoint. So expect some big dependencies, or a good layer of handwritten boilerplate, to plug your existing API onto your favorite server as a set of REST endpoint.

每一个网页框架都有它自己定义URL端点的方式。所以能够预料把你的现有API插入到你的喜欢的服务器上作为REST的端点需要很大的依赖,或者需要很好的一层手写的模板。

Libraries like Django-Rest-Framework automate the creation of REST APIs, by acting as data-centric wrappers above SQL/noSQL schemas. If you just want to make “CRUD over HTTP”, you could be fine with them. But if you want to expose common “do-this-for-me” APIs, with workflows, constraints, complex data impacts and such, you’ll have a hard time bending any REST framework to fit your needs.

像Django-Rest-Framework一样的库能够在SQL或NOSQL的大纲之上做数据密集的封装,使REST API的创建自动化。如果你仅仅想通过HTTP实现查插删改,你对他们会满意的。如果你想暴露一些常见的“为我做这件事”这样的有工作流、限制、复杂数据影响等的API,你很难发现满足你需求的REST框架。

Be prepared to connect, one by one, each HTTP method of each endpoint, to the corresponding method call; with a fair share of handmade exception handling, to translate passing-through exceptions into corresponding error codes and payloads.

准备好一个个地连接每个端点的HTTP方法到函数调用;还需要考虑很多手工异常处理,且把通过的异常翻译为错误值和载荷。

How to industrialize client-side integration?

如何工业化客户端的集成?

From experience, my guess is: you don’t.

依照经验,我猜是:不要这样做。

For each API integration, you’ll have to browse lengthy docs, and follow detailed recipes on how each of the N possible operations has to be performed.

对每个API的集成,你需要浏览长的文档,遵照详细的如何进行多种操作的配方。

You’ll have to craft URLs by hand, write serializers and deserializers, and learn how to workaround the ambiguities of the API. Expect quite some trial-and-error before you tame the beast.

你不得不手工打磨URL,写序列化和反序列化代码,学会如何解决API的歧义性。在你驯服这个怪兽前准备好多次试错。

Do you know how webservices providers make up for this, and ease adoption?

你知道网络服务的提供者如何弥补这一点来简化使用的吗?

Simple, they write their own official client implementations.

简单,他们自己写官方的客户端实现。

FOR. EVERY. MAJOR. LANGUAGE. AND. PLATFORM.

为每一种主流语言和平台。

I’ve recently dealt with a subscription management system. They provide clients for PHP, Ruby, Python, .NET, iOS, Android, Java… plus some external contributions for Go and NodeJS.

最近我处理过一个订阅管理系统。他们提供PHP, Ruby, Python, .NET, IOS, Android, Java…再加上一些外部贡献的Go和NodeJS的客户端。

Each client lives in its own Github repository. Each which its own big list of commits, bug tracking tickets, and pull requests. Each with its own usage examples. Each with its own awkward architecture, somewhere between ActiveRecord and RPC proxy.

每一种客户端都有它自己的Github代码库。每一个都有它自己的很长的列表的提交、缺陷跟踪、合并请求。每一个都有它自己的使用例子。每一个都有它自己的尴尬的介于ActiveRecord和RPC代理之间的架构,

This is astounding. How much time is spent developing such weird wrappers, instead of improving the real, the valuable, the getting-stuff-done, webservice?

这是令人震惊的。开发这些怪异的封装,而不是提高真正的有价值的解决问题的网络服务,得花多少时间啊?

[picture 8]

[图片 8]

Sisyphus developing Yet Another Client for his API.

西西弗斯在开发另一个它的API的客户端

Conclusion

结论

For decades, about every programming language has functioned with the same workflow: sending inputs to a callable, and getting results or errors as output. This worked well. Quite well.

有几十年了,大概每种语言都有相同的工作流程来保证功能:给被调用者提供输入,得到结果或错误作为输出。这工作得很好,非常好。

With Rest, this has turned into an insane work of mapping apples to oranges, and praising HTTP specifications to better violate them minutes later.

有了REST后,这变成了把苹果映射为橘子的精神病似的工作,赞扬HTTP规范然后很快就违反他们。

In an era where MICROSERVICES are more and more common, how come such an easy task?—?linking libraries over networks?—?remains so artificially crafty and cumbersome?

在微服务变得越来越普遍的时代,为什么这样简单的工作–通过网络映射库函数–仍然这样人为地费工夫和难处理?

I don’t doubt that some smart people out there will provide cases where REST shines; they’ll showcase their homemade REST-based protocol, allowing to discover and do CRUD operation on arbitrary object trees, thanks to hyperlinks; they’ll explain how the REST design is so brilliant, that I’ve just not read enough articles and dissertations about its concepts.

我不怀疑有一些聪明人可以提供REST工作良好的案例;他们可以展示他们自产的基于REST的协议,允许通过超链接来对任意对象树做查插删改的操作;他们会解释REST的设计是多么的绝妙,以至于我都没有读过足够关于这些概念的文章和论文。

I don’t care. Trees are recognized by their own fruits. What took me a few hours of coding and worked very robustly, with simple RPC, now takes weeks and can’t stop inventing new ways of failing or breaking expectations. Development has been replaced by tinkering.

我不关心。什么树结什么果。用简单的RPC只会花费我数小时的编码就可以工作得很稳健,现在需要花几周还不能阻止发明新的错误或不能实现预期。开发变成了修补。

Almost-transparent remote procedure call was what 99% people really needed, and existing protocols, as imperfect as they were, did the job just fine. This mass monomania for the lowest common denominator of the web, HTTP, has mainly resulted in a huge waste of time and grey matter.

几乎透明的远程方法调用是99%的人真正需要的。现有的协议即使不完美也可以很好地完成工作。这些大量的追求万维网和HTTP的最小公分母的偏执狂导致了大量的时间和脑力(大脑和神经是灰色的)的浪费。

REST promised simplicity and delivered complexity.

REST承诺简单交付复杂。

REST promised robustness and delivered fragility.

REST承诺健壮交付脆弱。

REST promised interoperability and delivered heterogeneity.

REST承诺互操作交付异类。

REST is the new SOAP.

REST是新的SOAP。

Epilogue

结束语

The future could be bright. There are still tons of excellent protocols available, in binary or text format, with or without schema, some leveraging the new abilities of HTTP2… so let’s move on, people. We can’t forever remain in the Stone Age of Webservices.

未来可能是光明的。还是有不管有没有大纲的二进制或文本格式的许多优秀的协议的,还有一些利用了HTTP版本2的新能力…让我们向前看,人类。我们不能呆在网络服务的石器时代。

Edit: many people asked for these alternative protocols, the subject would deserve its own story, but one could have a look at XMLRPC and JSONRPC (simple but quite relevant), or JSONWSP (includes schemas), or language-specific layers like Pyro or RMI when for internal use, or new kids in the block like GraphQL and gRPC for public APIs…

编辑:许多人问这些其他的协议,这个主题值的另一篇文章,但是感兴趣的可以看看XMLRPC和JSONRPC(简单但是非常有意义),或者看看JSONWSP(JSON Web Service Protocol),或者只是内部使用的话可以看看特定语言的方案比如Pyro或者RMI(Remote Method Invocation),或者如果提供公开API的话,看看这个街区的新孩子比如GraphQL和gRPC。

[picture 9]

[图片 9]

“Always finish a rant on a positive note”, momma said.

妈妈说:“总是以积极的调子来结束一场激昂的演讲。

 类似资料: