所以,我浏览了一些关于创建 REST API 的文章。其中一些建议使用所有类型的 HTTP 请求:例如 PUT
DELETE
POST
GET
。例如,我们将创建 index.php 并以这种方式编写 API:
$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));
switch ($method) {
case 'PUT':
....some put action....
break;
case 'POST':
....some post action....
break;
case 'GET':
....some get action....
break;
case 'DELETE':
....some delete action....
break;
}
好的,理所当然 - 我对 Web 服务知之甚少(还)。但是,通过常规 POST
或 GET
(包含方法名称和所有参数)接受 JSON 对象然后也以 JSON 响应不是更容易吗?我们可以通过 PHP 的 json_encode()
和 json_decode()
轻松地序列化/反序列化,并对这些数据做任何我们想做的事情,而无需处理不同的 HTTP 请求方法。
我错过了什么吗?
更新 1:
好的 - 在研究了各种 API 并了解了很多关于 XML-RPC、JSON-RPC、SOAP、REST 我得出的结论是这种类型的 API 是合理的。实际上堆栈交换在他们的网站上几乎使用这种方法,我认为这些人知道他们在做什么Stack Exchange API。
REpresentational State Transfer 的想法不是以最简单的方式访问数据。
您建议使用发布请求来访问 JSON,这是访问/操作数据的一种完全有效的方式。
REST 是一种有意义的数据访问方法。当您在 REST 中看到请求时,应该立即清楚数据发生了什么。
例如:
GET: /cars/make/chevrolet
很可能会返回一份雪佛兰汽车的清单。 一个好的 REST api 甚至可以在查询字符串中包含一些输出选项,例如
?output=json
或 ?output=html
,这将允许访问者决定信息应该以什么格式编码。
在考虑了如何合理地将数据类型合并到 REST API 中之后,我得出结论,明确指定数据类型的最佳方法是通过已经存在的文件扩展名,例如 .js
、.json
、 .html
或 .xml
。缺少的文件扩展名将默认为任何默认格式(例如 JSON);不受支持的文件扩展名可能会返回 501 Not Implemented
status code。
另一个例子:
POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }
很可能会在数据库中创建一个带有相关颜色的新雪佛兰马里布。我说可能是因为 REST api 不需要与数据库结构直接相关。它只是一个屏蔽接口,以便保护真实数据(将其视为数据库结构的访问器和修改器)。
现在我们需要讨论 idempotence 的问题。通常 REST 通过 HTTP 实现 CRUD。 HTTP 对请求使用 GET
、PUT
、POST
和 DELETE
。
一个非常简单的 REST 实现可以使用以下 CRUD 映射:
Create -> Post
Read -> Get
Update -> Put
Delete -> Delete
这个实现存在一个问题: Post 被定义为非幂等方法。这意味着后续调用相同的 Post 方法将导致不同的服务器状态。 Get、Put 和 Delete 是幂等的;这意味着多次调用它们应该会导致相同的服务器状态。
这意味着一个请求,例如:
Delete: /cars/oldest
实际上可以实现为:
Post: /cars/oldest?action=delete
然而
Delete: /cars/id/123456
如果您调用它一次,或者如果您调用它 1000 次,将导致相同的服务器状态。
处理删除 oldest
项的更好方法是请求:
Get: /cars/oldest
并使用结果数据中的 ID
发出 delete
请求:
Delete: /cars/id/[oldest id]
如果在请求 /oldest
和发出 delete
之间添加了另一个 /cars
项,则此方法会出现问题。
这是一个安全性和可维护性问题。
安全方法
只要有可能,您应该使用“安全”(单向)方法,例如 GET 和 HEAD,以限制潜在的漏洞。
幂等方法
只要有可能,您应该使用“幂等”方法,例如 GET、HEAD、PUT 和 DELETE,它们不会产生副作用,因此更不容易出错/更容易控制。
简而言之,REST 强调名词而不是动词。随着您的 API 变得越来越复杂,您会添加更多的东西,而不是更多的命令。
您问:
通过普通 $_POST 接受 JSON 对象然后也以 JSON 响应不是更容易吗
来自 REST 上的 Wikipedia:
RESTful 应用程序最大限度地利用预先存在的、定义良好的接口和所选网络协议提供的其他内置功能,并最大限度地减少在其之上添加新的应用程序特定功能
从我所看到的(很少)来看,我相信这通常是通过最大限度地利用现有 HTTP 动词并为您的服务设计一个尽可能强大且不言而喻的 URL 方案来实现的。
不鼓励自定义数据协议(即使它们构建在标准协议之上,例如 SOAP 或 JSON),并且应该最小化以最好地符合 REST 意识形态。
另一方面,基于 HTTP 的 SOAP RPC 鼓励每个应用程序设计人员定义一个新的、任意的名词和动词词汇表(例如 getUsers()、savePurchaseOrder(...)),通常覆盖在 HTTP 'POST' 动词上。这忽略了 HTTP 的许多现有功能,例如身份验证、缓存和内容类型协商,并可能让应用程序设计人员在新词汇表中重新发明许多这些功能。
您正在使用的实际对象可以是任何格式。这个想法是尽可能多地重用 HTTP 来公开用户想要对这些资源执行的操作(查询、状态管理/突变、删除)。
您问:
我错过了什么吗?
还有很多关于 REST 和 URI 语法/HTTP 动词本身的知识。例如,有些动词是幂等的,有些则不是。我在您的问题中没有看到任何关于此的内容,因此我没有费心尝试深入研究。其他答案和维基百科都有很多很好的信息。
此外,如果您使用的是真正的 RESTful API,您可以利用这些构建在 HTTP 之上的各种网络技术。我将从身份验证开始。
关于使用扩展来定义数据类型。我注意到 MailChimp API 正在这样做,但我认为这不是一个好主意。
GET /zzz/cars.json/1
GET /zzz/cars.xml/1
我的想法听起来不错,但我认为“旧”方法更好——使用 HTTP 标头
GET /xxx/cars/1
Accept: application/json
此外,HTTP 标头对于跨数据类型通信要好得多(如果有人需要它)
POST /zzz/cars
Content-Type: application/xml <--- indicates we sent XML to server
Accept: application/json <--- indicates we want get data back in JSON format
我错过了什么吗?
是的。 ;-)
这种现象的存在是因为uniform interface constraint。 REST 喜欢使用已经存在的标准,而不是重新发明轮子。 HTTP 标准已经被证明是高度可扩展的(网络已经运行了一段时间)。我们为什么要修复没有损坏的东西?!
注意:如果您想将客户端与服务分离,统一接口约束很重要。它类似于为类定义接口以便将它们彼此分离。办公室。在这里,统一接口由 HTTP、MIME types、URI、RDF、linked data vocabs、hydra vocab 等标准组成......
良好的语义在编程中很重要。
使用除 GET/POST 之外的更多方法会很有帮助,因为它会增加代码的可读性并使其更易于维护。
为什么?
因为您知道 GET 将从您的 api 中检索数据。您知道 POST 会将新数据添加到您的系统中。您知道 PUT 会进行更新。 DELETE 将删除行等,等等,
我通常构建我的 RESTFUL Web 服务,以便我有一个与方法名称相同的函数回调。
我使用 PHP,所以我使用 function_exists (我认为它被称为)。如果该函数不存在,我会抛出 405(不允许使用方法)。
Bill Venners:在您题为“为什么 REST 失败”的博文中,您说我们需要所有四个 HTTP 动词——GET、POST、PUT 和 DELETE——并感叹浏览器供应商只需要 GET 和 POST。”为什么我们需要所有四个动词?为什么 GET 和 POST 还不够?
Elliotte Rusty Harold:HTTP 中有四种基本方法:GET、POST、PUT 和 DELETE。大部分时间都使用 GET。它用于任何安全且不会引起任何副作用的事物。 GET 能够被添加书签、缓存、链接到、通过代理服务器。这是一个非常强大的操作,非常有用的操作。
相比之下,POST 可能是最强大的操作。它可以做任何事情。对于可能发生的事情没有限制,因此,您必须非常小心。你没有给它添加书签。你不缓存它。你不预先获取它。在不询问用户的情况下,您不会对 POST 做任何事情。你想这样做吗?如果用户按下按钮,您可以发布一些内容。但是您不会查看页面上的所有按钮,然后开始随机按下它们。相比之下,浏览器可能会查看页面上的所有链接并预获取它们,或者预获取他们认为接下来最有可能被关注的链接。事实上,一些浏览器和 Firefox 扩展程序以及其他各种工具都曾尝试过这样做。
PUT 和 DELETE 位于 GET 和 POST 之间。 PUT 或 DELETE 与 POST 的区别在于 PUT 和 DELETE 是*幂等的,而 POST 不是。如有必要,可以重复 PUT 和 DELETE。假设您正在尝试将新页面上传到站点。假设您想在 http://www.example.com/foo.html 创建一个新页面,因此您键入内容并将其放在该 URL 中。服务器在您提供的那个 URL 上创建该页面。现在,让我们假设由于某种原因您的网络连接中断。您不确定,请求是否通过?可能网络很慢。可能是代理服务器问题。因此,再试一次或再试一次是完全可以的——你喜欢多少次都可以。因为将同一个文档放置到同一个 URL 十次与放置一次没有什么不同。 DELETE 也是如此。您可以 DELETE 十次,这与删除一次相同。
相比之下,POST 每次都可能导致不同的事情发生。想象一下,您正在通过按下购买按钮从在线商店结账。如果您再次发送该 POST 请求,您最终可能会再次购买购物车中的所有物品。如果您再次发送它,您已经购买了第三次。这就是为什么浏览器必须非常小心地在未经用户明确同意的情况下重复 POST 操作,因为如果执行两次 POST 可能会导致两件事发生,如果执行三次可能会导致三件事发生。使用 PUT 和 DELETE,0 个请求和 1 个请求有很大区别,但 1 个请求和 10 个请求没有区别。
请访问网址了解更多详情。 http://www.artima.com/lejava/articles/why_put_and_delete.html
更新:
幂等方法 幂等 HTTP 方法是一种可以多次调用而不会产生不同结果的 HTTP 方法。该方法是否只被调用一次或十次都没有关系。结果应该是一样的。同样,这仅适用于结果,而不适用于资源本身。这仍然可以被操纵(如更新时间戳,只要此信息未在(当前)资源表示中共享)。
考虑以下示例:
a = 4;一个++;
第一个例子是幂等的:无论我们执行多少次这条语句,a总是4。第二个例子不是幂等的。执行 10 次将产生与运行 5 次不同的结果。由于这两个示例都在更改 a 的值,因此它们都是非安全方法。
POST: /cars/oldest
代替 DELETE 没有多大意义。像 -POST: /cars/oldest/delete
这样的东西可能,但我认为我更喜欢 Neil 的解决方案。与他的 get-id-delete-id 解决方案相比,直接删除的唯一优势是原子性。在我实施这样的事情之前,我想要一个明确的商业理由和一个非人为的场景。您不需要支持所有对象/url 上的所有动词。