2

若你曾写过 REST API,你肯定体验过那些无休止的争论,例如该使用哪个动词?该使用哪个 HTTP 状态码?该如何组织我的 URL?这些都没有正确答案。不是所有的操作都能由某个动词表示。不是所有的错误都匹配 HTTP 错误。为何我们要在 API 上使用那些为超文本设计的动词和错误码?我们使用 HTTP 作为客户端/服务端的通信方式是因为它是如此普遍,在它上面增加 REST 可能是杀鸡用牛刀了,特别当你只是想要一个简单的 API 接口。

思考这个典型的 API 在其他语言中的调用以及它是如何转化为 REST。

OrderDTO Customer::GetOrder(int customerID, int orderID)

作为一个 REST 调用应该类似这样

GET /customer/33245/order/8769

如果我们将方法名置于变量之间,那么问题就来了,该 URL 的哪部分是终点?哪些是参数?这些参数又与什么对应?很难仅仅通过 URL 推断出它是如何传递给后端的。

一个简单的 API,与我们原始的方法调用很像,没有混淆参数名称,并且清晰定义了要传递的参数

GET /customer/getOrder?customerID=33245&orderID=8769

作为一位 API 用户或是设计师,上面的 URL 更容易被理解,因为方法调用和参数被清晰的分离开来。这并不是 RESTful,但是除此之外在编程中哪里还用过如此类似 REST 的模式?有语言强制要求使用 4 个动词来修饰吗?这些真的必要吗?简单的说,这不是必要的。POST 解决一切。

REST URL 看起来『更干净』,但它们更容易被理解吗?SEO 不是必要的。当你希望扩展更多参数,或使用更加复杂的参数例如数组,这些『简单』的 URL 会让你很头疼。

GET /customer/33245/order/8769

为何不把参数从 URL 中移除?将它们放在 query string,或以 JSON 格式 POST 过去。这样子参数被显示命名,更易理解,消除了对复杂路由属性的依赖。

POST /customer/getOrder
{ customerID: 33245, orderID: 8769 }

错误码

为何要用为接收超文本而设计的数量有限的错误码来表示你的错误响应?

保存来自服务器自身错误的 HTTP 特定错误码。例如你得到一个 404 响应,那么它来自服务器还是你的应用程序?一个简化的 API 可能在成功时返回 HTTP 200 ,在遇到错误时返回 500 和应用级错误码。

这些代码可以是为业务定制的(订单未找到,摄像头未找到,数据库跪了,已打包等等)。代码可以是唯一以方便追溯。测试中结合特定应用错误码可以提高测试准确性。

POST /shipment/cancel 
{ orderID: 567, reason: ‘Wrong size’ }

400 Response
{ appError: 1023, message: ‘Order already shipped’, showUser: true }

如果你觉得 HTTP 响应应该是 500 而不是 400,那么这只是这篇文章的部分观点。关于怎么做是『对』的争论一直存在。

超媒体即应用状态引擎 (HATEOAS)

REST 和 RESTful 之间的一个核心区别在于 HATEOAS 的实现。一般是在你的 API 请求响应中发送相关 API 的 URL。如果你的 API 以一个银行的存款响应了,它也包含用来取钱的 URL。这种方式下 API 是可发现和可遍历的。在其他语言中 API 调用不返回方法指针指向相关的方法。所以又一次发问,为何我们将 API 搞得如此复杂?仅仅因为我们我们通过 HTTP 来执行它们还是为了适应别人的学说?

版本控制

另一个经常实现很多魔法的地方就是 API 版本控制方案。如果一个单一方法需要版本控制那么它可以简单的以 /user/getProfile2 形式实现,为何要用其他不直接的方法?如果控制器入口需要控制版本,可以简单的类似于 /user2/getProfile 。难道需要任何更复杂的东西吗?没有特殊的路由,没有魔法并且直接映射到你的后端控制器和方法。

NoREST!

一个 URL 应该清晰定义了位置,终点和参数。无需复杂的路由代码来分离参数。或是通过动词区分意图。

动词无关性,HTTP 动词真的只应该用来识别参数的去向 - 对 GET 来说它在 query string,对于 POST 它在 body。

版本控制应具体到被修改的事物。向下兼容易于维护,无需特殊的映射或属性。

HTTP 错误码,简化为 200/400。response body 包含应用特定错误码和消息。

具象状态传输,例如

GET /user/getProfile

可能返回一个 ProfileDTO (具象状态) 能被更新和作为一个参数通过下面的 API 回传至服务器

POST /user/setProfile

没有人在正确的 URL,动词或状态码上达成一致意见。REST 是一种风格,而不是一种标准。在远程 web 服务器上调用方法应该是一个简单的 RPC。 方法名称不和参数混合。方法不用动词修饰。参数都在一个单独的地方。简单。


原文:who-cares-about-get-vs-post-norest by swax

作者:swax


王铁手
4.1k 声望279 粉丝

怀着一颗铁匠的心,凭借一双铁手,打造自己的人生。