API 端点是不是可以根据用户凭据 RESTful 和良好的 URI 设计来区分要返回的资源?

Posted

技术标签:

【中文标题】API 端点是不是可以根据用户凭据 RESTful 和良好的 URI 设计来区分要返回的资源?【英文标题】:Is an API endpoint that differentiates what resources to return based on user credentials RESTful and good URI design?API 端点是否可以根据用户凭据 RESTful 和良好的 URI 设计来区分要返回的资源? 【发布时间】:2015-05-14 08:15:10 【问题描述】:

重要提示

这个问题的重点是区分根据谁进行身份验证返回哪些资源的 API 端点,例如Alice 得到资源 A 和 B 返回,Bob 得到资源 X 和 Y。

不是要区分返回的资源的表示。

所有端点都返回资源的 JSON 表示形式。

前言

请考虑以下三种潜在的 API 端点设计,它们都返回用户的 thing 资源。

端点 A

GET /things

如果在请求中提供了<user_x> 的身份验证凭据,它会返回与<user_x> 专门相关的thing 资源。 例如,认证用户 Alice 得到资源 A 和 B,认证用户 Bob 得到资源 X 和 Y。

因此,不同身份验证用户的响应差异在于返回哪些资源实例,而返回这些实例的哪些信息(即资源表示)。

身份验证失败时返回 401 响应。

端点 B

GET /user/<user_x>/things

端点 C

GET /things/?user_id=<user_x>

端点 B 和 C 都提供与 &lt;user_x&gt; 相关的 thing 资源实例,iff 身份验证用户有权访问这些 thing 资源。

返回的thing 资源实例的表示形式,例如返回的有关资源的哪些信息可能因用户身份验证而异。例如,&lt;user_x&gt; 或管理员用户可能会在每个资源实例中获得比访问权限有限的用户更丰富的数据。

验证对&lt;user_x&gt;thing 资源没有任何访问权限的用户将得到 401 响应。

我的问题

我想回答以下问题:

1) 端点是 RESTful 的吗?

2) 端点 A 是否有良好的 URI 设计?

3) 端点 B 和 C 是 RESTful 的吗?

4) 端点 B 和 C 是否具有良好的 URI 设计?

期待您的回答。我还在下面提供了my own answers,也将不胜感激。

谢谢!

——弗莱迪·斯奈德

【问题讨论】:

您的问题向我表明您不了解 RESTful 意味着什么。 REST 是一种主要基于原则和模式的整体架构风格(即方法)。因此,询问特定端点是否为 RESTful 是没有意义的。 另外,不管你信不信,你设计和组织 URI 的方式与 REST 无关。 REST 的“大问题”是要求在响应中传递链接,从而为客户端提供进一步导航资源的可能性。虽然对 URI 进行有意设计可能是有益的,但它们应该被客户端视为完全不透明。反过来,客户端应该只根据收到的关系导航链接,而不是一些关于如何构建 URI 的先入为主的概念。 Jonathan W,很抱歉,但我认为您的评估不公平。我确实了解 REST。当我描述端点时,我还描述了它们的行为方式(服务如何处理对这些端点的请求以及它如何响应)。因此,当我询问端点 X 是否为 RESTful 时,我会询问所描述的行为是否符合 REST 原则和模式。幂等性和安全性等行为也是与 REST 相关的重要概念;我将自己的答案集中在这些概念上。确实,我没有提到任何有关在响应中传递的链接。 Jonathan W,关于您对 URI 的评论:这就是为什么我有两种类型的问题:一种与端点行为的 RESTful 相关,另一种与 URI 设计有关,这确实是一个单独的主题。 Jonathan W,我的问题的重点实际上是关于根据谁进行身份验证做出响应(这里我的意思是返回哪些资源集)的优点。这可以通过端点 A 举例说明。虽然,如果您考虑一下,纯粹从 REST 的角度来看,这似乎并不重要。但实际上,我认为最好只使用 B 和 C 等端点,因为它们通过 URI 明确说明了 API 用户的需求。我希望得到有关这方面的反馈。尽管您和其他人制作的 cmets 对于任何对 REST 感兴趣的人来说都非常有价值。 【参考方案1】:

更新于 2015 年 3 月 18 日 13:05 CET,将反馈包含在给出的问题和答案的 cmets 中。

宁静

从纯粹的角度来看,没有一个端点是 RESTful 的。例如,问题没有说明响应是否包含指向资源的链接,以便客户端可以检索资源而无需了解如何构造资源的 URI。事实上,as pointed out in this blogpost,除了万维网本身之外,几乎没有在实践中定义的 API 可以被认为是 RESTful。

那么关于这些端点没有什么有用的说法吗?我认为有。 我们可以讨论处理端点的无状态和幂等性,这对可扩展性很重要。我们可以谈论对安全很重要的端点的安全性。

对于所有端点,您可以声明以下内容:

是无状态的吗?

是的,用户身份验证凭据是应用程序状态的一部分,并随每个请求一起发送,因此服务器在不保持状态的情况下处理请求所需知道的一切都在请求中。 (完整状态转移)

由于这些端点处理 GET 请求,它们是否具有同等效力?

端点 A) :是的,因为对端点 A 的请求,包括用户身份验证凭据,应该被视为一个整体:无论您多久重复一次相同的请求,使用相同的凭据,您总是会得到 @ 987654323@ 认证用户的资源。

但是,如果只考虑 URI,则请求实际上并不具有同等效力,因为响应会根据提供的凭据而变化。

端点 B) 和 C) :与 A) 类似,您将始终获得 URI 中提供的 &lt;user_x&gt;thing 资源,无论您多久重复一次。

除此之外,仅考虑 URI 本身,请求也是等效的,您需要了解的有关请求的所有信息都在 URI 中,用户凭据只能更改返回的 thing 资源的表示,而不是哪个资源被退回。

由于这些端点处理 GET 请求,它们安全吗?

是的,因为请求不会改变任何数据,也不会产生任何其他副作用。

URI 设计

虽然从纯粹的 REST 角度来看,URI 设计被认为是无关紧要的,但在软件开发人员和 API 最终用户使用和处理 URI 设计的实际情况中是相关的

端点 A 是否有良好的 URI 设计?

是和否。如果此 URI 对应用程序用户隐藏,并且不会被添加书签或共享,则此设计很好。然而,当这个 URI 暴露给最终用户时,这个 URI 设计得不好,因为当作为链接共享它时,收件人不会看到相同的数据,除非她以同一用户身份进行身份验证。

端点 B 和 C 是否有良好的 URI 设计?

是的,最终用户可以从语义上理解端点的含义,并且 URI 可以在用户之间共享。

因此,您可以选择只定义端点 B 和 C,而不是定义所有三个端点,因为它们可以提供端点 A 可以提供的一切,而且从 URL 中可以明显看出所请求的内容。

请告诉我你的想法。谢谢!

——弗莱迪·斯奈德

【讨论】:

为什么 API 用户需要从语义上理解 URI?如果他们这样做,他们就没有关注链接,而是将自己编码到一个 URI 角落,可能。或者 API 没有返回链接,这意味着 API 不是 RESTful。 您好 Jonathan,“为什么 API 用户需要从语义上理解 URI?”。对我来说,这就像在问为什么我们在编程时给变量起语义上有用的名字?我确实理解您关于返回资源链接的要求,使一切变得透明。想象一下他们确实返回了这些链接,那么最终我的问题似乎归结为在定义 URI 时语义是否重要?我认为是这样。对于系统解析它不是,对于程序员它是。同意吗? 不,抱歉。我不同意。程序员应该专门致力于理解有据可查的资源关系,并且真的不应该关心 URI 的样子。 JonathanW, jfcorugedo, @inf3rno 我更新了自己的答案,以反映 cmets 对给出的问题和答案所做的。感谢大家的意见和讨论。 我还认为,对于端点 A,您总是希望为当前登录的用户获取东西,这是可取的。以inbox.google.com 为例。如果我与您分享此链接,我的意图几乎肯定是不是让您访问我的收件箱,而是提醒您您的收件箱在哪里。这可能是您唯一的要求。而且,如果您需要提供额外的访问权限,没有什么可以说您将来也不能创建 B/C 样式的端点来启用该行为。【参考方案2】:

这是一个非常好的问题,也是我在为我正在从事的项目开发 API 时一直在问自己的确切问题。我对 REST 比较陌生,因此正在经历陡峭的学习曲线,因此远非专家!

我一直在阅读 Subbu Allalaraju 的“RESTful Web Services Cookbook”。

值得这些是我的想法,它们与您自己提供的答案非常相似。在实践中,我确信身份验证凭据会影响为同一 URI 返回的信息是一种常见情况。凭据实际上只是另一个查询参数。

1) 端点是 RESTful 的吗?

是的,它是无状态的,因为身份验证凭据是从客户端与请求一起传递的。

根据https://www.w3.org/Protocols/HTTP/1.0/spec.html 第 10.2 节授权 “对包含授权字段的请求的响应不可缓存。” 因此相同的凭据将始终检索相同的结果(身份验证用户的列表事物)。

2) 端点 A 是否有良好的 URI 设计?

不,它实际上与端点 B 或 C 相同,用户请求检索自己的东西,但灵活性和清晰度较低。

3) 端点 B 和 C 是 RESTful 的吗?

是的,它们是无状态且幂等的。

4) 端点 B 和 C 是否具有良好的 URI 设计?

是的,我认为两者都可能是有效的设计。对于每种方法的优缺点,都有无休止的讨论!

我选择在我自己的 API 中使用类似的东西。

GET /user/01/things(以用户 01 身份正确验证)

Response 200 OK: Yes user 01 当然你可以看到你自己的东西。

GET /user/01/things(以用户 02 身份正确验证)

响应 200 OK:是的,用户 02,您是超级用户,可以查看用户 01 拥有的东西(或者可能是其中的一部分)。

GET /user/01/things(以用户 03 身份正确验证)

Response 403 Forbidden: No user 03 即使你已经正确认证你只是一个普通用户,看不到用户 01 有什么东西。或者你可以返回 Response 200 OK 并在响应数据中包含类似的信息,这一切都取决于你如何设计你的 API 以及在这种情况下你需要能够传回多少信息,我认为这两种方法都是有效的。

GET /user/01/things(错误地验证为用户 01)

响应 401 未经授权:

List of HTTP status codes

【讨论】:

【参考方案3】:

我修改了我的旧答案。我假设我们谈论的是网络文档,所以/things/1 标识的是网络文档,而不是真实的单词。 (在此处阅读更多信息:http://en.wikipedia.org/wiki/Dereferenceable_Uniform_Resource_Identifier 和此处:https://www.rfc-editor.org/rfc/rfc6920。)

    端点是 RESTful 的吗?

    端点 A 是否有良好的 URI 设计?

    端点 B 和 C 是 RESTful 的吗?

    端点 B 和 C 是否具有良好的 URI 设计?

问题 1 和 3 的答案是肯定的,您可以将同一资源的不同表示形式发送给具有不同权限的用户。 (如果你想缓存这些响应,你应该使用可变标头来做到这一点。)

问题 2 和 4 的答案取决于您如何定义“良好的 URI 设计”。您的 URI 完全有效,并且由于 REST 没有任何 URI 结构约束,并且没有关于如何为不同应用程序设计 REST URI 的标准(除了 ofc。URI 标准和 URI 模板标准),我会说它们很好。

这里有一个类似的问题:RESTful URL design: public vs private API, hierhachy API design pattern, URI vs URL design? 关于这个分层与平面 URI 问题,如果您想阅读我对此的更详细的意见。

【讨论】:

嗨@inf3rno,我不确定你是否在回答我的问题。在端点 A 的情况下,返回资源的 表示 保持不变,但返回的 哪些 资源因授权者而异。如果我正确阅读了您的第一段,您是在说这是不受欢迎的行为,对吗?如果是这样,这只是糟糕的 URI 设计还是实际上违反了 REST 原则? @Visionscaper Ofc。我没有回答你的问题,因为它们在这种情况下没有意义。我编辑了我的答案,也许现在更容易理解了。 嗨@inf3rno 你能解释为什么我的问题没有意义吗?我解释了潜在端点是如何定义的以及它们的行为方式。然后我问他们是否以 RESTful 方式设计以及 URI 是否设计得好。我不明白这有什么问题。可以肯定的是,我改进了我的问题的文本,也使我的问题的焦点更加清晰:API 端点根据谁进行身份验证来区分返回哪些资源,而不是返回哪种表示。谢谢你的时间! 1) 感谢您提供指向可取消引用的 URI 的链接,它很有用。这是否意味着端点 A 至少应该被定义为,例如,/things/list 以便将它(在这种情况下)作为一个列表来尊重? @Visionscaper 所有的 A.) B.) C.) 都是好的 URI 设计。我更喜欢 A.) 列出整个集合(如果没有给出凭据,则为空集合)和部分集合(如果给出凭据,但无权列出整个集合)。如果其他人(或用户自己)想要列出其他用户拥有的东西,则使用 B.) 或 C.)。办公室。您必须将诸如事物和拥有事物之类的元数据添加到链接中,以便客户端知道链接的含义。您可以使用可取消引用或不可取消引用的 URI 来描述这些术语。 URI 可以指向开发者文档。【参考方案4】:

首先,在 HTTP 中,资源的表示由标头 Content-Type 和 Accept 处理。

但客户只能建议他们喜欢的格式。服务器负责提供他想要的表示。

谈到安全性,我根据令牌的安全级别返回一个表示是完全正确的。

谈到你的 URL,我总是更喜欢 URL 参数而不是查询参数(有些引擎不会缓存带有查询参数的 GET 请求)。

但这取决于您的数据结构。如果 user_idthings 中,则应在 URL 中表示:/things/user_id/xxxxx。

另一方面,如果 user_id 与资源 things 无关,请考虑删除此参数并将其添加为标头(注意 GET 请求和缓存时间)。

【讨论】:

您好 jfcorugedo,感谢您的回答。我发现关于引擎没有使用查询参数缓存请求的评论很有帮助!但是,恕我直言,您没有回答我的问题。我改进了我的问题的文本,使我的问题的重点更加清晰:它是关于 API 端点,它根据谁进行身份验证来区分返回哪些资源,例如Alice 得到资源 A 和 B 返回,而 Bob 得到资源 X 和 Y。这不是区分返回资源的表示。欢迎您的反馈! 好的,在这种情况下,我认为可以根据安全性向用户发送不同的表示形式。但是,当用户为您的网址添加书签时要小心

以上是关于API 端点是不是可以根据用户凭据 RESTful 和良好的 URI 设计来区分要返回的资源?的主要内容,如果未能解决你的问题,请参考以下文章

通过 Spring 进行 RESTful 身份验证

如何设计一个 RESTful API 来检查用户的凭据?

Rails RESTful API + 设计 - 如何检查用户的凭据

一个restful api可以从一个端点中删除两个相关资源吗?

使用 RESTful 登录 API 验证我的 Spring Boot 应用程序

GAE上的RESTful API:端点 - 原型数据存储区与云端点