ChatGPT解决这个技术问题 Extra ChatGPT

了解 REST:动词、错误代码和身份验证

我正在寻找一种将 API 包装在基于 PHP 的 Web 应用程序、数据库和 CMS 中的默认函数的方法。

我环顾四周,发现了几个“骨架”框架。除了我的问题中的答案之外,还有 Tonic,一个我喜欢的 REST 框架,因为它非常轻量级。

我最喜欢 REST 的简单性,并希望基于它创建一个 API 架构。我试图了解基本原理,但还没有完全理解它。因此,提出了一些问题。

1.我理解对了吗?

假设我有一个资源“用户”。我可以像这样设置许多 URI:

/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

到目前为止,这是 RESTful 架构的正确表示吗?

2. 我需要更多的动词

创建、更新和删除在理论上可能就足够了,但实际上我需要更多的动词。我意识到这些是可以嵌入到更新请求中的东西,但它们是可以具有特定返回码的特定操作,我不想将它们全部放入一个操作中。

在用户示例中想到的一些是:

activate_login
deactivate_login
change_password
add_credit

我将如何表达诸如 RESTful URL 架构中的操作?

我的直觉是对像这样的 URL 进行 GET 调用

/api/users/1/activate_login 

并期望返回状态码。

但是,这偏离了使用 HTTP 动词的想法。你怎么看?

3.如何返回错误信息和代码

REST 的美妙之处很大一部分源于它对标准 HTTP 方法的使用。出现错误时,我会发出一个带有 3xx、4xx 或 5xx 错误状态代码的标头。对于详细的错误描述,我可以使用正文(对吗?)。到目前为止,一切都很好。但是,传输专有错误代码的方式是什么,该代码更详细地描述出了什么问题(例如“无法连接到数据库”或“数据库登录错误”)?如果我将它与消息一起放入正文中,我必须在之后将其解析出来。这种事情有标准的标题吗?

4.如何进行认证

遵循 REST 原则的基于 API 密钥的身份验证是什么样的?

在对 REST 客户端进行身份验证时,是否有反对使用会话的优点,除了它公然违反 REST 原则? :) (这里只是在开玩笑,基于会话的身份验证可以很好地与我现有的基础设施配合使用。)

@Daniel,感谢您的编辑。 “I more verbs”是故意的双关语,但我保留原样,现在更容易阅读。 :)
顺便说一句,关于错误描述。我最终将错误描述放在响应的标题中。只需添加名为“错误描述”的标题。
这看起来更像是应用程序安全问题。应用程序安全不是 REST 的意义所在。
@NazarMerza 1.、2. 和 3. 应用程序安全问题如何?

W
Woodchuck

我晚了几天才注意到这个问题,但我觉得我可以添加一些见解。我希望这对您的 RESTful 冒险有所帮助。

第1点:我理解对了吗?

你理解的没错。这是 RESTful 架构的正确表示。您可能会发现 Wikipedia 中的以下矩阵对定义名词和动词非常有帮助:

处理 Collection URI 时,例如:http://example.com/resources/

GET:列出集合的成员,并附上他们的成员 URI,以便进一步导航。例如,列出所有待售汽车。

PUT:含义定义为“用另一个集合替换整个集合”。

POST:在集合中创建一个新条目,其中 ID 由集合自动分配。创建的 ID 通常包含在此操作返回的数据中。

DELETE:含义定义为“删除整个集合”。

处理 Member URI 时,例如:http://example.com/resources/7HOU57Y

GET:检索以适当的 MIME 类型表示的集合的寻址成员的表示。

PUT:更新集合的寻址成员或使用指定的 ID 创建它。

POST:将被寻址的成员本身视为一个集合,并为其创建一个新的下属。

DELETE:删除集合的寻址成员。

第 2 点:我需要更多动词

一般来说,当你认为你需要更多的动词时,实际上可能意味着你的资源需要重新识别。请记住,在 REST 中,您总是对资源或资源集合进行操作。您选择什么作为资源对于您的 API 定义非常重要。

激活/停用登录:如果您正在创建一个新会话,那么您可能需要将“会话”视为资源。要创建新会话,请使用 POST 到 http://example.com/sessions/ 并在正文中使用凭据。要使其过期,请使用 PUT 或 DELETE(可能取决于您是否打算保留会话历史记录)到 http://example.com/sessions/SESSION_ID

更改密码:这次资源是“用户”。您需要 PUT 到 http://example.com/users/USER_ID,并在正文中包含旧密码和新密码。您正在对“用户”资源进行操作,更改密码只是一个更新请求。它与关系数据库中的 UPDATE 语句非常相似。

我的直觉是对 /api/users/1/activate_login 之类的 URL 进行 GET 调用

这违背了一个非常核心的 REST 原则:HTTP 动词的正确使用。任何 GET 请求都不应留下任何副作用。

例如,GET 请求永远不应该在数据库上创建会话,返回带有新会话 ID 的 cookie,或者在服务器上留下任何残留物。 GET 动词类似于数据库引擎中的 SELECT 语句。请记住,当使用相同的参数请求时,使用 GET 动词对任何请求的响应都应该是可缓存的,就像您请求静态网页时一样。

Point 3:如何返回错误信息和代码

将 4xx 或 5xx HTTP 状态代码视为错误类别。您可以详细说明正文中的错误。

无法连接到数据库:/不正确的数据库登录:通常您应该对这些类型的错误使用 500 错误。这是服务器端错误。客户没有做错任何事。 500 个错误通常被认为是“可重试的”。即客户端可以重试相同的请求,并期望一旦服务器的问题得到解决,它就会成功。在正文中指定详细信息,以便客户端能够为我们人类提供一些上下文。

另一类错误是 4xx 系列,通常表明客户端做错了什么。特别是,此类错误通常向客户端表明无需按原样重试请求,因为它将继续永久失败。即客户端需要在重试此请求之前更改某些内容。例如,“找不到资源”(HTTP 404) 或“格式错误的请求”(HTTP 400) 错误将属于此类别。

第4点:如何进行身份验证

正如第 1 点所指出的,您可能需要考虑创建会话,而不是验证用户。您将收到一个新的“会话 ID”以及相应的 HTTP 状态代码(200:已授予访问权限或 403:已拒绝访问)。

然后,您将询问您的 RESTful 服务器:“您能给我获取此会话 ID 的资源吗?”。

没有经过身份验证的模式 - REST 是无状态的:您创建一个会话,您要求服务器使用此会话 ID 作为参数为您提供资源,然后在注销时删除或使会话过期。


很好,但是您使用 PUT 更改密码可能不正确; PUT 需要整个资源,因此您必须发送所有用户属性才能符合 HTTP(因此符合 HATEOAS REST)。相反,要简单地更改密码,应该使用 PATCHPOST
我认为,如果您进一步扩展“POST:将寻址成员本身视为一个集合并创建它的新下属”,我认为这篇文章将是完美的。方法。 - 我通过谷歌搜索找到了它的含义 - 这是你原本很好的答案的一个例外。
我不同意最后一句话。您正在解释 REST 如何是无状态的。登录以创建会话,然后在完成一些工作后注销以结束会话是有状态 API 的最佳示例。
“这违背了一个非常核心的 REST 原则:正确使用 HTTP 动词。任何 GET 请求都不应留下任何副作用。” - 如果您想保持资源的命中计数怎么办?
这篇文章应该回答你的问题。 saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
L
Lawrence Dol

简而言之,您正在完全倒退。

你不应该从你应该使用的 URL 来处理这个问题。一旦您决定了系统需要哪些资源、如何表示这些资源以及资源和应用程序状态之间的交互,这些 URL 将有效地“免费”提供。

引用Roy Fielding

REST API 应该花费几乎所有的描述性工作来定义用于表示资源和驱动应用程序状态的媒体类型,或定义扩展关系名称和/或现有标准媒体类型的超文本启用标记。描述在感兴趣的 URI 上使用哪些方法所花费的任何努力都应该完全在媒体类型的处理规则范围内定义(并且在大多数情况下,已经由现有媒体类型定义)。 [这里的失败意味着带外信息正在推动交互而不是超文本。]

人们总是从 URI 开始并认为这是解决方案,然后他们往往会错过 REST 架构中的一个关键概念,特别是如上所述,“这里的失败意味着带外信息正在驱动交互而不是超文本。 "

老实说,许多人看到一堆 URI 和一些 GET、PUT 和 POST,并认为 REST 很简单。 REST 并不容易。 RPC over HTTP 很容易,通过 HTTP 有效负载来回移动数据块很容易。然而,REST 不止于此。 REST 与协议无关。 HTTP 非常流行并且适用于 REST 系统。

REST 存在于媒体类型、它们的定义以及应用程序如何通过超文本(有效地链接)驱动这些资源可用的操作。

关于 REST 系统中的媒体类型有不同的看法。有些人喜欢特定于应用程序的有效负载,而另一些人则喜欢将现有媒体类型提升为适合应用程序的角色。例如,一方面,您拥有适合您的应用程序的特定 XML 模式,而不是使用 XHTML 之类的东西作为您的表示,可能通过微格式和其他机制。

我认为,这两种方法都有自己的位置,XHTML 在与人类驱动和机器驱动的 web 重叠的场景中工作得非常好,而我觉得前者更具体的数据类型更好地促进了机器对机器的交互。我发现商品格式的提升可能会使内容谈判变得困难。 “application/xml+yourresource”作为一种媒体类型比“application/xhtml+xml”更具体,因为后者可以应用于许多负载,这些负载可能是机器客户端实际感兴趣的东西,也可能不是不自省地确定。

然而,XHTML 在 Web 浏览器和呈现非常重要的人类 Web 中工作得非常好(显然)。

您的申请将指导您做出这些决定。

设计 REST 系统的过程的一部分是发现系统中的第一类资源,以及支持对主要资源的操作所必需的衍生支持资源。一旦发现资源,然后是这些资源的表示,以及通过表示中的超文本显示资源流的状态图,因为这是下一个挑战。

回想一下,在超文本系统中,资源的每个表示都将实际资源表示与资源可用的状态转换结合在一起。将每个资源视为图中的一个节点,链接是将该节点留给其他状态的线。这些链接不仅告诉客户可以做什么,还告诉他们需要做什么(因为一个好的链接结合了 URI 和所需的媒体类型)。

例如,您可能有:

<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

您的文档将讨论名为“users”的 rel 字段,以及“application/xml+youruser”的媒体类型。

这些链接可能看起来是多余的,它们几乎都在与同一个 URI 对话。但他们不是。

这是因为对于“用户”关系,该链接正在谈论用户的集合,您可以使用统一接口来处理集合(GET 检索所有用户,DELETE 删除所有用户等)

如果您发布到此 URL,您将需要传递一个“application/xml+usercollection”文档,该文档可能仅包含文档中的单个用户实例,因此您可以添加用户,或者不添加多个一次。也许您的文档会建议您可以简单地传递单个用户类型,而不是集合。

您可以查看应用程序执行搜索所需的内容,如“搜索”链接及其媒体类型所定义。搜索媒体类型的文档将告诉您其行为方式以及预期结果。

不过,这里的要点是 URI 本身基本上并不重要。应用程序控制着 URI,而不是客户端。除了一些“入口点”之外,您的客户应该依赖应用程序提供的 URI 来完成工作。

客户端需要知道如何操作和解释媒体类型,但并不需要关心它的去向。

在客户眼中,这两个链接在语义上是相同的:

<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

所以,专注于你的资源。关注他们在应用程序中的状态转换以及如何最好地实现。


感谢威尔的这个非常深刻的回答。拿了几个点。我意识到从“URL 看起来像什么”进行规划是相反的,我也在从资源方面进行规划。使用 URL 只是让我更容易理解这个概念。可能我的要求可以通过一个不 100% 遵循您在此处定义的 REST 原则的系统来满足。我将为每种资源类型绘制完整的需求列表,我想我将能够做出决定。干杯。
B
BradChesney79

re 1:到目前为止这看起来不错。请记住在“Location:”标头中返回新创建用户的 URI,作为对 POST 的响应的一部分,以及“201 Created”状态代码。

re 2:通过 GET 激活是个坏主意,在 URI 中包含动词是一种设计味道。您可能需要考虑在 GET 上返回表单。在 Web 应用程序中,这将是一个带有提交按钮的 HTML 表单;在 API 用例中,您可能希望返回包含要 PUT 的 URI 的表示来激活帐户。当然,您也可以在对 /users 的 POST 响应中包含此 URI。使用 PUT 将确保您的请求是幂等的,即如果客户端不确定是否成功,它可以安全地再次发送。一般来说,想想你可以把你的动词变成什么资源(有点像“动词的名词化”)。问问自己,你的具体行动与哪种方法最接近。例如 change_password -> PUT;停用 -> 可能删除; add_credit -> 可能是 POST 或 PUT。通过将它们包含在您的表示中,将客户端指向适当的 URI。

re 3. 不要发明新的状态代码,除非您认为它们非常通用,值得在全球范围内标准化。努力使用可用的最合适的状态码(在 RFC 2616 中阅读所有这些状态码)。在响应正文中包含其他信息。如果你真的,真的确定你想发明一个新的状态码,再想一想;如果您仍然相信,请确保至少选择正确的类别(1xx -> OK,2xx -> 信息,3xx -> 重定向;4xx-> 客户端错误,5xx -> 服务器错误)。我有没有提到发明新的状态码是个坏主意?

重新 4. 如果可能,请使用 HTTP 中内置的身份验证框架。查看 Google 在 GData 中进行身份验证的方式。通常,不要将 API 密钥放在 URI 中。尽量避免会话以增强可伸缩性并支持缓存 - 如果对请求的响应由于之前发生的某些事情而不同,您通常会将自己绑定到特定的服务器进程实例。最好将会话状态转换为客户端状态(例如使其成为后续请求的一部分)或通过将其转换为(服务器)资源状态使其显式,即给它自己的 URI。


您能讨论一下为什么不将 API 密钥放在 URL 中吗?是因为它们在代理日志中可见吗?如果密钥是瞬态的、基于时间的怎么办?如果使用 HTTPS 会怎样?
除了违反精神(URI应该识别事物)之外,主要的后果是它破坏了缓存。
f
friedo

1. 恕我直言,您对如何设计资源有正确的想法。我不会改变任何事情。

2. 与其尝试使用更多动词来扩展 HTTP,不如考虑根据基本 HTTP 方法和资源可以将您提议的动词简化为什么。例如,代替 activate_login 动词,您可以设置如下资源:/api/users/1/login/active,它是一个简单的布尔值。要激活登录,只需 PUT 一个显示“true”或 1 或其他内容的文档。要停用,PUT 那里的文档是空的,或者说 0 或 false。

同样,要更改或设置密码,只需对 /api/users/1/password 执行 PUT

每当您需要添加某些内容(例如信用)时,请根据 POST 进行思考。例如,您可以对像 /api/users/1/credits 这样的资源执行 POST,其正文包含要添加的积分数。同一资源上的 PUT 可用于覆盖值而不是添加。正文中带有负数的 POST 将减去,依此类推。

3. 我强烈建议不要扩展基本的 HTTP 状态代码。如果您找不到与您的情况完全匹配的一个,请选择最接近的一个并将错误详细信息放在响应正文中。另外,请记住 HTTP 标头是可扩展的;您的应用程序可以定义您喜欢的所有自定义标头。例如,我处理的一个应用程序可能会在多种情况下返回 404 Not Found。我们没有让客户端解析响应正文,而是添加了一个新标头 X-Status-Extended,其中包含我们专有的状态代码扩展。因此,您可能会看到如下响应:

HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

这样一来,像网络浏览器这样的 HTTP 客户端仍然知道如何处理常规 404 代码,而更复杂的 HTTP 客户端可以选择查看 X-Status-Extended 标头以获取更具体的信息。

4.对于认证,如果可以的话,我推荐使用HTTP认证。但是恕我直言,如果这对您来说更容易,那么使用基于 cookie 的身份验证并没有错。


使用“扩展”资源对较大资源的较小部分执行操作的巧妙想法。
cookie 在 HTTP/REST 中有效,但服务器不应将 cookie 存储为状态(因此不作为会话)。然而,cookie 可以存储一个像 HMAC 一样的值,可以在不查找其他地方的状态的情况下对其进行反汇编。
C
Community

REST 基础

REST 有一个统一的接口约束,它规定 REST 客户端必须依赖于标准,而不是实际 REST 服务的特定于应用程序的细节,因此 REST 客户端不会因微小的更改而中断,并且它可能是可重用的。

因此,REST 客户端和 REST 服务之间存在契约。如果您使用 HTTP 作为底层协议,那么以下标准是合同的一部分:

HTTP 1.1

方法定义

状态码定义

缓存控制头

接受和内容类型标头

身份验证标头

IRI (utf8 URI)

身体(选一个)

注册的应用程序特定 MIME 类型,例如 maze+xml

供应商特定的 MIME 类型,例如 vnd.github+json

具有应用程序特定 RDF 词汇的通用 MIME 类型,例如 ld+json 和 hydra,schema.org 应用程序特定配置文件,例如 hal+json 和配置文件链接参数(我猜)

特定于应用程序的 RDF 词汇,例如 ld+json & hydra、schema.org

特定于应用程序的配置文件,例如 hal+json 和配置文件链接参数(我猜)

超链接 应该包含它们的内容(选择一个) 在链接头中发送 在超媒体响应中发送,例如 html、atom+xml、hal+json、ld+json&hydra 等...语义使用 IANA 链接关系,并且可能自定义链接关系使用应用特定的 RDF 词汇

什么应该包含它们(选择一个)在链接头中发送发送超媒体响应,例如 html、atom+xml、hal+json、ld+json&hydra 等...

发送链接头

发送超媒体响应,例如 html、atom+xml、hal+json、ld+json&hydra 等...

语义使用 IANA 链接关系,并且可能自定义链接关系使用特定于应用程序的 RDF 词汇

使用 IANA 链接关系和可能的自定义链接关系

使用特定于应用程序的 RDF 词汇

REST 有一个无状态约束,它声明 REST 服务和客户端之间的通信必须是无状态的。这意味着 REST 服务无法维护客户端状态,因此您无法拥有服务器端会话存储。您必须对每个请求进行身份验证。因此,例如 HTTP 基本身份验证(HTTP 标准的一部分)是可以的,因为它会在每个请求中发送用户名和密码。

回答你的问题

是的,可以。

顺便提一下,客户端不关心 IRI 结构,他们关心语义,因为他们遵循具有链接关系或链接数据 (RDF) 属性的链接。

IRI 唯一重要的是,单个 IRI 必须仅标识单个资源。允许单个资源(例如用户)拥有许多不同的 IRI。

为什么我们使用像 /users/123/password 这样好的 IRI 非常简单;当您通过阅读 IRI 来了解 IRI 时,在服务器上编写路由逻辑要容易得多。

你有更多的动词,比如 PUT、PATCH、OPTIONS,甚至更多,但你不需要更多……你必须学习如何添加新资源,而不是添加新动词。

   deactivate_login -> PUT /login/active false
   change_password -> PUT /user/xy/password "newpass"
   add_credit -> POST /credit/raise {details: {}}

(从 REST 的角度来看,登录没有意义,因为无状态约束。)

您的用户不关心问题存在的原因。他们只想知道是成功还是错误,并且可能是他们可以理解的错误消息,例如:“对不起,但我们无法保存您的帖子。”等...

HTTP 状态标头是您的标准标头。我认为其他一切都应该在体内。单个标头不足以描述例如详细的多语言错误消息。

无状态约束(连同缓存和分层系统约束)确保服务可很好地扩展。当您可以在客户端上执行相同操作时,您肯定不想在服务器上维护数百万个会话……如果用户使用主客户端授予对它的访问权限,则第 3 方客户端将获得访问令牌。之后,第 3 方客户端随每个请求发送访问令牌。还有更复杂的解决方案,例如您可以签署每个请求等。有关更多详细信息,请查看 OAuth 手册。

相关文献

架构风格和基于网络的软件架构的设计 Roy Thomas Fielding 的论文(REST 的作者)2000 年,加州大学欧文分校

第三代 Web API - 弥合 REST 与关联数据之间的差距 Markus Lanthaler(JSON-LD 的合著者和 Hydra 的作者)2014 年,奥地利格拉茨科技大学


j
jonnii

对于您所说的示例,我将使用以下内容:

激活登录

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

更改密码

PUT /passwords(假设用户已通过身份验证)

add_credit

POST /credits(假设用户已通过身份验证)

对于错误,您将以收到请求的格式在正文中返回错误,因此如果您收到:

DELETE /users/1.xml

您将以 XML 格式发回响应,JSON 等也是如此……

对于身份验证,您应该使用 http 身份验证。


我不会将 create 用作 URI 的一部分(请记住,URI 应该是名词,HTTP 方法应该是对这些名词进行操作的动词。)相反,我会有一个像 /users/1/active 这样的资源,它可以是一个简单的布尔值,可以通过将 1 或 0 放入该资源来设置。
你是对的,我取出了/create。它应该只是单例资源的帖子。
我也不会在 URI 上使用 activation,除非您以 /users/1/activation 的名称显式操作和管理资源。 GET 有什么作用? PUT 有什么作用?在我看来,您确实是在对 URI 进行动词化。此外,对于内容类型协商,通常最好将其排除在 URI 之外并插入到标头中,例如 Accept
A
Arjan

当您不知道新资源 URI 的样子时使用 post(您创建新用户,应用程序将为新用户分配它的 id),PUT 用于更新或创建您知道它们将如何表示的资源(例如: PUT /myfiles/thisismynewfile.txt) 在消息体中返回错误描述 你可以使用 HTTP 认证(如果够用的话) Web 服务应该是 stateles


B
Brian Agnew

我建议(作为第一遍)PUT 应该只用于更新现有实体。 POST 应该用于创建新的。 IE

/api/users     when called with PUT, creates user record

我觉得不对。但是,第一部分的其余部分(关于动词用法)看起来合乎逻辑。


可能有人认为这不是他问题的真正答案
我对创建新实体的 PUT 与 POST 的看法是,当调用者控制资源名称时使用 PUT,因此您可以 PUT 到确切的资源并在被调用者控制新资源名称时使用 POST(例如此处的示例)。
g
gahooa

冗长,但从 http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html 处的 HTTP 1.1 方法规范复制而来

9.3 获取

GET 方法意味着检索由 Request-URI 标识的任何信息(以实体的形式)。如果 Request-URI 指的是数据生成过程,则生成的数据应作为响应中的实体返回,而不是过程的源文本,除非该文本恰好是该过程的输出。

如果请求消息包含 If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match 或 If-Range 头字段,则 GET 方法的语义更改为“条件 GET”。条件 GET 方法请求仅在条件头字段描述的情况下传输实体。条件 GET 方法旨在通过允许刷新缓存的实体而不需要多个请求或传输客户端已经持有的数据来减少不必要的网络使用。

如果请求消息包含 Range 标头字段,则 GET 方法的语义更改为“部分 GET”。部分 GET 请求仅传输实体的一部分,如第 14.35 节所述。部分 GET 方法旨在通过允许完成部分检索的实体而不传输客户端已经持有的数据来减少不必要的网络使用。

当且仅当它满足第 13 节中描述的 HTTP 缓存要求时,对 GET 请求的响应是可缓存的。

用于表单时的安全注意事项见第 15.1.3 节。

9.5 发布

POST 方法用于请求源服务器接受请求中包含的实体,作为 Request-Line 中 Request-URI 标识的资源的新下级。 POST 旨在允许统一的方法涵盖以下功能:

  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

POST 方法执行的实际功能由服务器确定,通常取决于 Request-URI。发布的实体从属于该 URI,就像文件从属于包含它的目录一样,新闻文章从属于发布它的新闻组,或者一条记录从属于数据库。

POST 方法执行的操作可能不会产生可由 URI 标识的资源。在这种情况下,200(正常)或 204(无内容)是适当的响应状态,具体取决于响应是否包含描述结果的实体。

如果在源服务器上创建了资源,则响应应该是 201(已创建)并包含描述请求状态并引用新资源的实体和 Location 标头(参见第 14.30 节)。

对此方法的响应是不可缓存的,除非响应包含适当的 Cache-Control 或 Expires 标头字段。但是,303(请参阅其他)响应可用于指示用户代理检索可缓存资源。

POST 请求必须遵守第 8.2 节中规定的消息传输要求。

有关安全考虑,请参阅第 15.1.3 节。

9.6 放

PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果 Request-URI 引用了一个已经存在的资源,封闭的实体应该被认为是在源服务器上的一个修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。如果创建了新资源,源服务器必须通过 201 (Created) 响应通知用户代理。如果修改了现有资源,则应发送 200(OK)或 204(No Content)响应代码以指示请求成功完成。如果无法使用 Request-URI 创建或修改资源,则应给出反映问题性质的适当错误响应。实体的接收者不得忽略任何它不理解或不实现的 Content-*(例如 Content-Range)标头,并且在这种情况下必须返回 501(未实现)响应。

如果请求通过缓存并且 Request-URI 标识了一个或多个当前缓存的实体,那么这些条目应该被视为陈旧的。对此方法的响应不可缓存。

POST 和 PUT 请求的根本区别体现在 Request-URI 的不同含义上。 POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个通往其他协议的网关,或者一个接受注释的单独实体。相比之下,PUT 请求中的 URI 标识了请求中包含的实体——用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。如果服务器希望将请求应用于不同的 URI,

它必须发送 301(永久移动)响应;然后用户代理可以自己决定是否重定向请求。

单个资源可以由许多不同的 URI 标识。例如,一篇文章可能有一个用于标识“当前版本”的 URI,它与标识每个特定版本的 URI 是分开的。在这种情况下,对通用 URI 的 PUT 请求可能会导致源服务器定义其他几个 URI。

HTTP/1.1 没有定义 PUT 方法如何影响源服务器的状态。

PUT 请求必须遵守第 8.2 节中规定的消息传输要求。

除非对特定的实体标头另有规定,否则 PUT 请求中的实体标头应该应用于由 PUT 创建或修改的资源。

9.7 删除

DELETE 方法请求源服务器删除由 Request-URI 标识的资源。此方法可能会被源服务器上的人工干预(或其他方式)覆盖。即使从源服务器返回的状态码表明操作已经成功完成,客户端也不能保证操作已经执行。但是,服务器不应指示成功,除非在给出响应时它打算删除资源或将其移动到无法访问的位置。

如果响应包含描述状态的实体,则成功的响应应该是 200(OK),如果操作尚未制定,则为 202(已接受),如果操作已经制定但响应不包括,则应为 204(无内容)一个实体。

如果请求通过缓存并且 Request-URI 标识了一个或多个当前缓存的实体,那么这些条目应该被视为陈旧的。对此方法的响应不可缓存。


M
Marcodor

关于 REST 返回码:HTTP 协议码和 REST 结果混用是错误的。

但是,我看到许多实现混合了它们,许多开发人员可能不同意我的看法。

HTTP 返回码与 HTTP Request 本身有关。 REST 调用是使用超文本传输协议请求完成的,它的工作级别低于调用的 REST 方法本身。 REST 是一种概念/方法,其输出是业务/逻辑结果,而 HTTP 结果代码是传输结果。

例如,当您调用 /users/ 时返回“404 Not found”是令人困惑的,因为它可能意味着:

URI 错误 (HTTP)

未找到用户 (REST)

“403 Forbidden/Access Denied”可能意味着:

需要特别许可。浏览器可以通过询问用户/密码来处理它。 (HTTP)

服务器上配置了错误的访问权限。 (HTTP)

您需要经过身份验证(REST)

并且该列表可能会继续出现“500 服务器错误”(Apache/Nginx HTTP 抛出错误或 REST 中的业务约束错误)或其他 HTTP 错误等...

从代码中,很难理解失败的原因是什么,是 HTTP(传输)失败还是 REST(逻辑)失败。

如果 HTTP 请求在物理上成功执行,它应该总是返回 200 代码,无论是否找到记录。因为 URI 资源已找到并由 http 服务器处理。是的,它可能会返回一个空集。是否有可能收到一个 200 作为 http 结果的空网页,对吗?

取而代之的是,您可以返回 200 个 HTTP 代码和一个带有空数组/对象的 JSON,或者使用布尔结果/成功标志来通知执行的操作状态。

此外,一些互联网提供商可能会拦截您的请求并向您返回 404 http 代码。这并不意味着找不到您的数据,而是在传输级别有问题。

Wiki

2004 年 7 月,英国电信供应商 BT Group 部署了 Cleanfeed 内容阻止系统,该系统会对 Internet Watch Foundation 确定为潜在非法内容的任何请求返回 404 错误。其他 ISP 在相同情况下返回 HTTP 403“禁止”错误。泰国和突尼斯也报道了使用虚假 404 错误作为隐藏审查的手段的做法。在 2011 年革命前审查制度严厉的突尼斯,人们开始意识到假 404 错误的本质,并创造了一个名为“Ammar 404”的虚构角色,代表“隐形审查员”。