【问题标题】:RESTful API without ID in the URLURL 中没有 ID 的 RESTful API
【发布时间】:2014-01-17 17:42:38
【问题描述】:

我一直在与我的一位同事讨论这样做的最佳方法

这是一个示例场景:

我正在尝试获取 ID 为 1234 的 Customer 的所有订单。

考虑以下端点:

/客户/订单

使用 GET 请求,带有以下标头:

授权:基本 QWxhZGRpbjpvcGVuIHNlc2FtZQ==

使用 Authorization 标头,我们的身份验证机制将此请求识别为 ID 为 1234 的客户,并且我们的服务返回所需的订单。

他试图说服我这是对的,因为登录的客户只会请求他们的订单(在这种情况下,订单属于客户 1234) - 所以传入 ID URL 是不必要的。 但是....对我来说,这不是 RESTful(我可能错了)

在我看来,应该是这样的:

/customer/1234/订单

带表头(以表头为例)

授权:基本 QWxhZGRpbjpvcGVuIHNlc2FtZQ==

通过 Authorization 标头,我们验证用户是否有权检索这些订单...如果是,则返回它们,否则,返回 401

我的问题是,哪个是首选方法?

我当然可以看到第一种方式的好处,但是为了保持我们的 api RESTful,我的心说要采用第二种方式......

【问题讨论】:

  • 你最后采用了什么方法?
  • 最终使用了带有 auth 标头的方法。效果很好

标签: rest asp.net-web-api restful-authentication restful-url restful-architecture


【解决方案1】:

资源的识别是一个关键的 REST 约束。您的订单和我的订单不是同一个资源,因此它们应该(不是必须)具有不同的标识符。

您应该给他们一个唯一标识符(即 /customers/1234/orders)的实际原因是因为支持 HTTP 缓存会容易得多。 HTTP 缓存中介主要使用 URI 作为缓存键。通过使用可变标头,可以使用其他标头(如 auth)作为缓存键的一部分,但是,对此类机制的支持不太可靠/广泛使用。

即使您今天没有使用 HTTP 缓存的计划,最好还是谨慎行事,并确保您的 URI 设计为在将来需要时允许此功能。

在构建客户端 URI 时,不必在 URI 中包含客户 ID 有一些好处。但是,RESTful 系统应该通过将这些 URI 嵌入到返回的表示中来为客户端提供遵循的 URI。客户端不需要构建这些 URI,因此客户端使用带有 id 的 URI 就像使用不带 Id 的 URI 一样需要做零额外的工作。如果你准备吞下超媒体药丸,那么不包括 ID 是没有好处的。

【讨论】:

  • 超媒体药丸坚持 RESTful 实现的“超媒体作为引擎”特性。 (wikipedia.org/wiki/HATEOAS) 但是,如果 Darrel 的论点是客户端将盲目地遵循来自服务器的超链接响应,因此没有理由不包含客户 ID,那么人们可以很容易地反驳 - 那不需要包含客户 ID,因为客户将盲目地跟踪超链接。在这种情况下,我会说为什么要为标准而烦恼?
  • @isick 通过在 URI 中添加 id,您可以为我的订单和您的订单使用不同的 URI。因此,缓存可以使用 URI 作为缓存键。
  • @isick 而且 RESTful 系统中没有 URI 设计标准。 URI 应该对客户端完全不透明。 URI 设计只是为了方便服务器端路由。
  • @DarrelMiller 我明白你在说什么,但我认为你的水平太深了。 Alex 请求的资源是“订单”。该资源的范围受客户 ID 的限制,但您的客户 ID 不能识别您请求的资源(即订单)。因此,在这个用例中既没有违反也没有遵守 RESTful 资源识别的要求。
  • @isick 你在考虑资源的概念。唯一的 URI 标识资源。 “订单”不是资源,“客户”也不是。 “example.org/customer/1234/orders”标识一个资源,“example.org/customer/5678/orders”标识一个不同的资源。资源的概念更接近于对象实例而不是类。
【解决方案2】:

我认为仅当您要允许客户检索其他客户的订单数据时才需要第二个实现(包括客户 ID)。换句话说,如果客户 4321 能够看到客户 1234 的订单历史记录,那么发送是绝对必要的。

我的猜测是他们无法做到这一点。这意味着如果您包含该信息,您很可能会忽略它而支持授权代码。

如果是这样的话,我说不要发送它。

如果它让您感觉更好,LinkedIn 的 REST API 会反映这种行为。任何用户都可以请求任何其他用户的个人资料但是,如果省略 id,则假定当前用户正在请求他们自己的个人资料。 Read more here

话虽如此,只要它对你有意义并且你记录它,你应该没问题。

【讨论】:

  • +1 是的,REST 中没有任何内容表明资源的 URI 应包含明确声明的 id。 URI 本身可以被视为标识符,其中包含的任何 id 都可以作为进一步的范围信息。
【解决方案3】:

两者都不是 RESTful。 URI 的语义应该与 RESTful 应用程序中的客户端无关。

REST 是一种基于 Web 本身的架构风格。想想看。当您访问 Stack Overflow 时,您唯一关心的 URI 是 stackoverflow.com。登录后,您将被重定向到主页,并且主页的 URI 无关紧要。现在我的菜单栏上有一个指向我主页的链接,你有一个相同的链接指向你的主页,当我们点击它时我们不在乎 URI 是什么,对吧?在我们的主页上,我们看到问题和答案,我们也不关心它们的 URI,只关心它们的标题和描述。

现在,让我们反转它。 REST 是 Web 的架构风格,如果 Web 就像您认为的 REST 那样,当您加入 stackoverflow.com 时,您的名字不会指向您的主页,而是有一个很好的链接,您会有一个文本说“您的id 是 131809,请取 URI 模式 /users/(id),将 id 替换为这个数字,粘贴到地址栏中,然后你就会到你的主页”,你会想,“为什么那些白痴不做个链接?”

这听起来很荒谬,但大多数人认为 REST 就是这样。 REST APIs must be hypertext driven.

这意味着您不必担心用户的订单是否应该在/customer/(customer_id)/orders,或者只是/customer/orders 甚至/orders 并依赖他的凭据,从而迫使客户端知道所有这些URI 模式,您应该只是在您的 API 入口点为他提供一个根文档,该文档会考虑他的凭据,并为 URI 提供标准标题和描述,就像您登录时任何网站一样!

假设您使用 JSON,并且您的 API 是 myapi.com。那应该是客户端知道的唯一 URI。当他使用正确的 Authorization 标头向该根文档发出 GET 请求时,他应该得到一个 JSON 文档,如下所示:

{"orders": "http://myapi.com/customer/123456/orders"}

显然,他不需要知道“orders”的值是什么,只需知道它是一个有效的 URI,它将引导他到另一个包含他的订单集合的资源。那个 URI 可以是任何东西,它的语义无关紧要。可能是你朋友的建议:

{"orders": "http://myapi.com/orders"}

或其他任何可以随时更改的内容:

{"orders": "http://myapi.com/this/is/a/meaningless/uri"}

当然,您应该付出一些努力来获得清晰且有意义的 URI,但这不会使您的 API 或多或少 RESTful。在这件事上,使您的 API 成为 RESTful 的是您的客户端如何获取 URI,如果您的客户端正在阅读文档中的 URI 模式而不是将它们作为其他资源中的链接获取,那么您的 API 不是 RESTful。就这么简单。

【讨论】:

  • 最后一种情况是您对 OPTIONS 的期望,而不是 GET 请求。
【解决方案4】:

如果您在其他地方使用/customer/[id] 模式中的 id,那么仅仅因为您认为没有其他人会看到它们而在订单中省略它会很奇怪且不一致。

如果客户看到的唯一订单是他们自己的订单,为什么不直接使用/orders?这样,至少可以避免这种不一致。

但是,我更愿意明确添加 id。如果将来客户支持人员想要查看订单,您将不得不更改 URL 或仅出于此目的创建一个新 URL。我建议将资源标识与访问权限条件严格分开。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 1970-01-01
    • 2013-01-06
    • 2018-02-11
    • 2012-06-02
    • 1970-01-01
    • 2015-08-25
    相关资源
    最近更新 更多