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

Understanding REST: Verbs, error codes, and authentication

提问人:Pekka 提问时间:1/5/2010 最后编辑:SandroMarquesPekka 更新时间:9/23/2017 访问量:143699

问:

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

我环顾四周,发现了几个“骨架”框架。除了我问题的答案之外,还有 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原则之外,是否有反对使用会话的优点?:)(这里只是半开玩笑,基于会话的身份验证可以很好地与我现有的基础架构配合使用。
Web 服务 REST

评论

17赞 Pekka 1/12/2010
@Daniel,感谢您的编辑。 “我更多的动词”是一个故意的双关语,但我保持原样,现在更容易阅读。:)
1赞 Andrii Muzychuk 3/10/2015
顺便说一句,关于错误描述。我最终将错误描述放在响应的标题中。只需添加名为“错误描述”的标题即可。
0赞 Nazar Merza 8/26/2016
这看起来更像是应用程序安全问题。应用程序安全不是REST的意义所在。
0赞 Pekka 8/27/2016
@NazarMerza 1., 2.和 3.应用程序安全问题?

答:

6赞 Brian Agnew 1/5/2010 #1

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

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

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

评论

0赞 lubos hasko 1/8/2010
可能有人认为这并不是他问题的答案
6赞 SteveD 1/10/2010
我对创建新实体的 PUT 与 POST 的看法是,当调用方控制资源名称时使用 PUT,因此您可以对确切的资源执行 PUT,在被调用方控制新资源名称时使用 POST(如此处的示例中)。
8赞 Arjan 1/5/2010 #2
  1. 当您不知道新资源 URI 的外观时(您创建新用户,应用程序将为其分配新用户的 ID),使用 PUT 来更新或创建您知道它们将如何表示的资源(例如:PUT /myfiles/thisismynewfile.txt)
  2. 在邮件正文中返回错误说明
  3. 您可以使用 HTTP 身份验证(如果足够) Web 服务应该是 stateles
13赞 jonnii 1/5/2010 #3

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

activate_login

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

change_password

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

add_credit

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

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

DELETE /users/1.xml

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

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

评论

1赞 friedo 1/5/2010
我不会用作 URI 的一部分(请记住,URI 应该是名词,HTTP 方法应该是对这些名词进行操作的动词。取而代之的是,我会有一个这样的资源,它可以是一个简单的布尔值,可以通过将 1 或 0 推到该资源来设置它。create/users/1/active
0赞 jonnii 1/5/2010
你是对的,我拿出了 /create。它应该只是对单例资源的帖子。
3赞 Cheeso 4/22/2010
我也不会在 URI 上使用,除非您将按名称 显式操作和管理资源。GET有什么作用?PUT有什么作用?在我看来,您确实在表达 URI。此外,对于内容类型协商,通常也最好将其排除在 URI 之外并插入到标头中,例如 .activation/users/1/activationAccept
6赞 gahooa 1/5/2010 #4

详细,但从 HTTP 1.1 方法规范中复制 http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

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 方法旨在通过允许在不传输客户端已持有的数据的情况下完成部分检索的实体来减少不必要的网络使用。

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

请参阅第 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 (OK) 或 204 (No Content) 是适当的响应状态,具体取决于响应是否包含描述结果的实体。

如果已在源服务器上创建资源,则响应应为 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(已创建)响应通知用户代理。如果修改了现有资源,则应发送 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 是分开的。在这种情况下,对常规 URI 的 PUT 请求可能会导致源服务器定义其他几个 URI。

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

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

除非为特定实体标头另行指定,否则 PUT 请求中的实体标头应应用于 PUT 创建或修改的资源。

9.7 删除

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

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

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

32赞 Stefan Tilkov 1/5/2010 #5

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

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

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

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

评论

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

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

2. 与其尝试用更多的动词来扩展 HTTP,不如考虑一下你提议的动词在基本的 HTTP 方法和资源方面可以简化为什么。例如,您可以设置如下资源,而不是动词: 这是一个简单的布尔值。要激活登录,只需一个显示“true”或 1 或其他内容的文档。要停用,该文档为空或显示 0 或 false。activate_login/api/users/1/login/activePUTPUT

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

每当您需要添加某些内容(例如信用)时,请考虑 s。例如,您可以对资源执行 a 操作,例如使用包含要添加的配额数的正文。同一资源上的 A 可用于覆盖值而不是添加值。正文中带有负数的 A 将减去,依此类推。POSTPOST/api/users/1/creditsPUTPOST

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

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

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

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

评论

4赞 womble 6/28/2011
使用“扩展”资源对较大资源的较小部分执行操作的巧妙想法。
1赞 Bruce Alderson 11/11/2011
Cookie 在 HTTP/REST 中有效,但服务器不应将 Cookie 存储为状态(因此不应存储为会话)。但是,cookie 可以像 HMAC 一样存储值,无需在其他地方查找状态即可反汇编该值。
661赞 Daniel Vassallo 1/8/2010 #7

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


第1点:我理解正确吗?

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


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

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

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

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

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


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

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

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

  • POST:将寻址成员本身视为一个集合,并创建它的新从属。

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


第2点:我需要更多的动词

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

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

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

我的直觉是做一个GET电话 到类似/api/users/1/activate_login

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

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


第 3 点:如何返回错误消息和代码

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

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

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


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

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

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

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

评论

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

简单地说,你完全倒退了。

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

引用罗伊·菲尔丁(Roy Fielding)的话

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

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

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

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

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

我认为,这两种方法都有其一席之地,XHTML在人类驱动和机器驱动的Web重叠的场景中运行良好,而前者,我认为更具体的数据类型更有利于机器之间的交互。我发现商品格式的提升可能会使内容谈判变得困难。“application/xml+yourresource”作为媒体类型比“application/xhtml+xml”更具体,因为后者可以应用于许多有效负载,这些有效负载可能是也可能不是机器客户端真正感兴趣的东西,也无法在没有内省的情况下确定。

然而,XHTML在人类网络中工作得很好(显然),因为网络浏览器和渲染非常重要。

您的应用程序将指导您做出这些决定。

设计 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 删除所有用户,等等)。

如果 POST 到此 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"/>

因此,请专注于您的资源。重点关注它们在应用程序中的状态转换以及如何最好地实现这种转换。

评论

2赞 Pekka 1/12/2010
感谢威尔的这个非常深刻的回答。有几点。我意识到从“URL 的样子”进行规划是相反的,我也是从资源方面进行规划。有 URL 可以玩只是让我更容易理解这个概念。可能是我的要求可以通过一个不是 100% 遵循您在此处定义的 REST 原则的系统来满足。我将列出每种资源类型的完整要求列表,我想我将能够决定。干杯。
15赞 inf3rno 9/18/2014 #9

REST 基础知识

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

因此,REST客户端和REST服务之间存在约定。如果使用 HTTP 作为基础协议,则以下标准是合同的一部分:

  • HTTP 1.1 协议
  • 方法定义
  • 状态代码定义
  • 缓存控制标头
  • Accept 和 Content-Type 标头
  • 身份验证标头
  • IRI(utf8 URI)
  • 正文(选择一个)
  • 已注册的应用程序特定 MIME 类型,例如 maze+xml
  • 特定于供应商的 MIME 类型,例如 vnd.github+json
  • 泛型 MIME 类型
    • 特定于应用程序的 RDF 词汇,例如 ld+jsonhydraschema.org
    • 特定于应用程序的配置文件,例如 hal+json 和配置文件链接参数(我猜)
  • 超链接
    • 应该包含它们的内容(选择一个)
      • 发送链接标头
      • 发送超媒体响应,例如.HTML,Atom + XML,HAL + JSON,LD + JSON&HYDRA等...
    • 语义学
      • 使用 IANA 链接关系,可能还有自定义链接关系
      • 使用特定于应用程序的 RDF 词汇

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

回答您的问题

  1. 是的,可以。

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

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

很简单,为什么我们使用像这样的好 IRI;当您只需阅读 IRI 即可理解 IRI 时,在服务器上编写路由逻辑要容易得多。/users/123/password

  1. 你有更多的动词,比如 PUT、PATCH、OPTIONS 等等,但你不需要更多......您必须学习如何添加新资源,而不是添加新动词。
   deactivate_login -> PUT /login/active false
   change_password -> PUT /user/xy/password "newpass"
   add_credit -> POST /credit/raise {details: {}}

(从REST的角度来看,由于无状态约束,登录名没有意义。

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

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

  1. 无状态约束(以及缓存和分层系统约束)可确保服务能够很好地扩展。你肯定不想在服务器上维护数百万个会话,而你可以在客户端上做同样的事情......

    如果用户使用主客户端授予对第三方客户端的访问权限,则第三方客户端将获得访问令牌。之后,第三方客户端会随每个请求发送访问令牌。还有更复杂的解决方案,例如,您可以签署每个请求等。有关更多详细信息,请查看 OAuth 手册。

相关文献

2赞 Marcodor 9/23/2017 #10

关于REST返回码:将HTTP协议代码和REST结果混为一谈是错误的

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

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

例如,在调用 /users/ 时返回“404 Not found”会造成混淆,因为它可能意味着:

  • URI 错误 (HTTP)
  • 未找到用户 (REST)

“403 禁止/拒绝访问”可能意味着:

  • 需要特殊许可。浏览器可以通过询问用户/密码来处理它。(HTTP的)
  • 在服务器上配置了错误的访问权限。(HTTP的)
  • 您需要进行身份验证 (REST)

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

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

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

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

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

来自维基

2004 年 7 月,英国电信提供商 BT Group 部署了 Cleanfeed 内容阻止系统,它向任何请求返回 404 错误 被互联网观察识别为可能非法的内容 基础。其他 ISP 在同一 ISP 中返回 HTTP 403“禁止”错误 情况 下。使用虚假 404 错误作为手段的做法 泰国和突尼斯也报告了隐瞒审查制度。在 突尼斯,在2011年革命之前审查制度很严格, 人们开始意识到假 404 错误的性质并创建 一个名叫“Ammar 404”的虚构人物,代表“看不见的人” 审查员”。