6

我正在尝试确定 REST API 中的最佳实践,以确定客户端是否可以访问特定资源。两个快速示例场景:

电话目录查找服务。客户通过访问例如查找电话号码。
GET http://host/directoryEntries/numbers/12345
...在12345目录中尝试查找的电话号码在哪里。如果存在,它将返回电话号码所在的人的姓名和地址等信息。

视频格式转换服务。客户将一种格式的视频提交给例如。
POST http://host/videos/
...并接收服务器为此视频生成的“视频 GUID”。客户然后检查例如。
GET http://host/videos/[GUID]/flv
... 获取视频,转换为 FLV 格式,如果转换后的版本存在。

您会注意到,在上述两种情况下,我都没有提到如果要检查的资源存在会发生什么。这是我的问题。我在其他各种 地方读到,客户端检查资源是否存在于此处的正确 RESTful 方式是调用(或可能)资源,如果资源不存在,它应该期待 404 响应。这很好,除了 404 响应被广泛认为是“错误”;HTTP/1.1 规范指出4xx 类状态码是用于客户端“似乎出错”的情况。可是等等; 在这些例子中,客户肯定没有犯错。它预计 HEADGET它可能会返回 404(或其他;如果未授权访问此资源,可能会返回 403),并且在请求资源时没有任何错误。404 并非旨在指示“错误条件”,它只是信息 - “这不存在”。

正如 HTTP 规范所暗示的,浏览器的行为就像 404 响应是一个真正的错误一样。每次 XHR 请求收到 404 时,无论是否由错误处理程序处理,Google Chrome 和 Firebug 的控制台都会在 Javascript 控制台中喷出一条大红色的“404 Not Found”错误消息,并且没有禁用它的方法。这对用户来说不是问题,因为他们看不到控制台,但作为开发人员,当我完全了解时,我不想在我的 JS 控制台中看到一堆 404(或 403 等)错误好吧,它们不是错误,而是由我的 Javascript 代码处理的信息。是线路噪音。在我给出的第二个示例中,线路噪音达到了极致,因为客户端可能会为此轮询服务器/flv因为编译可能需要一段时间,并且客户端希望显示“尚未编译”,直到它获得非 404。JS 控制台可能每隔一两秒就会出现 404 错误。

那么,这是我们使用 REST 检查资源是否存在的最佳或最合适的方法吗?我们如何绕过 JS 控制台中的线路噪音?很可能会建议,在我的第二个示例中,可以查询不同的 URI 来检查编译状态,例如:
GET http://host/videos/[GUID]/compileStatus
...但是,对我来说,这似乎有点违反 REST 原则;您没有完全使用 HTTP 并注意 HTTP 标头,而是创建自己的协议,通过该协议您在正文中返回信息,告诉您您想知道的内容,并始终返回 HTTP 200 以关闭浏览器. 这是对 SOAP 的主要批评——它试图“绕过”HTTP,而不是充分利用它。根据这一原则,为什么需要返回 404 状态码?您总是可以返回 200 - 当然,200 表示资源的状态信息可用,并且状态信息告诉您您真正想知道的内容 - 资源未找到。当然,RESTful 方式应该是返回 404 状态码。

如果我们将它应用到我上面的第一个例子中,这个机制似乎更加人为;客户可能会询问:
GET http://host/directoryEntries/numberStatuses/12345
...当然会收到 200;该号码12345状态信息存在,并告诉您...在目录中找不到该号码。这意味着查询的任何数字都是“200 OK”,即使它可能不存在——这看起来像是一个好的 REST 接口吗?

我错过了什么吗?是否有更好的方法来确定资源是否存在 RESTfully,或者是否应该更新 HTTP 以指示非 2xx 状态代码不一定被视为“错误”,而只是信息?浏览器是否应该能够进行配置,以便它们不会总是在 JS 控制台中将非 2xx 状态响应输出为“错误”?

PS。如果你读到这里,谢谢。;-)

4

4 回答 4

4

使用 404 表示未找到资源是完全可以的“RESTful Web Services”一书中的一些引用(顺便说一句,关于 REST 的非常好的书):

404 表示服务器无法将客户端的 URI 映射到资源。[...] Web 服务可以使用 404 响应作为向客户端发出 URI 是“免费”的信号;然后,客户端可以通过向该 URI 发送 PUT 请求来创建新资源。请记住,404 可能是掩盖 403 或 401 的谎言。可能是资源存在,但服务器不想让客户端知道它。

当服务找不到请求的资源时使用404,不要过度使用来指示实际上与资源存在无关的错误。此外,客户端可以“查询”服务以了解此 URI 是否免费。

执行长时间运行的操作,例如视频文件的编码

HTTP 有一个同步的请求-响应模型。客户端向服务器打开一个 Internet 套接字,发出请求,并保持套接字打开,直到服务器发送响应。[...]

问题不是所有的操作都可以在我们期望的 HTTP 请求的时间内完成。有些操作需要数小时或数天。在这种不活动之后,HTTP 请求肯定会超时。即使没有,谁愿意让套接字保持打开数天只是等待服务器响应?有没有办法通过HTTP异步暴露这样的操作?

有,但它要求将操作拆分为两个或多个同步请求。第一个请求产生操作,随后的请求让客户端了解操作的状态。秘密是状态码 202(“已接受”)。

所以你可以做POST /videos一个视频编码任务。该服务将接受任务,回答 202 并提供指向描述任务状态的资源的链接。

202 Accepted
Location: http://tasks.example.com/video/task45543

客户端可以查询此 URI 以查看任务的状态。一旦任务完成,资源的表示将变得可用。

于 2012-04-21T14:53:31.337 回答
2

我认为您已经更改了请求的语义。使用 RESTful 架构,您正在请求资源。因此,请求不存在或未找到的资源被视为错误。

我用:

  • 404 如果GET http://host/directoryEntries/numbers/12345不存在。

  • 400 实际上是一个错误的请求400 Bad Request

也许,在您的情况下,您可以考虑改为搜索。使用资源集合上的查询参数完成搜索

你想要的是 GET http://host/directoryEntries/numbers?id=1234 哪个将返回 200 和一个空列表(如果不存在)或匹配列表。

于 2012-04-21T14:27:50.367 回答
2

IMO 客户确实在请求不存在的资源时犯了错误。在您的两个示例中,可以以不同的方式设计服务,以便在客户端避免错误。例如,在已分配 GUID 的视频转换服务中,videos/id 处的消息正文可以包含指示转换是否完成的标志。

同样,在电话目录示例中,您正在搜索资源,这可以通过 /numbers/?search_number=12345 等方式处理,以便服务器返回匹配资源列表,然后您可以进一步查询。

浏览器是为使用 HTTP 规范而设计的,显示错误是真正的响应(也很有帮助)。但是,您需要将 Javascript 代码视为独立于浏览器的实体。所以你有你的 Javascript REST 客户端,它知道服务是什么样的,而浏览器对你的服务来说有点愚蠢。

此外,REST 在理论上独立于协议。HTTP 恰好是使用 REST 的最常见协议。我能想到的另一个例子是 Android 内容提供程序,它的设计是 RESTful 但不依赖于 HTTP。

于 2012-04-21T14:28:11.503 回答
0

我只见过 GET/HEAD 请求在资源不存在时返回 404(未找到)。我认为,如果您只想获取资源的状态,则头部请求会很好,因为它不应该返回资源的主体。通过这种方式,您可以区分您尝试检索资源的请求和您尝试检查其是否存在的请求。

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

编辑:我记得通过在原始请求中添加一个标头来指示服务器应如何处理 404 错误,从而了解了另一种解决方案。类似于用 200 响应的东西,但是一个空的身体。

于 2012-04-21T14:07:11.700 回答