【问题标题】:New(?) attept to structure RESTful base URLs新(?)尝试构建 RESTful 基本 URL
【发布时间】:2011-10-30 15:42:06
【问题描述】:

我们都喜欢 REST,尤其是在 API 开发方面。在过去的几年里,我总是偶然发现同样的问题:嵌套资源。似乎我们生活在天平的两个边缘。让我介绍一个例子。

/galaxies/8/solarsystems/5/planets/1/continents/4/countries.json

尼托。类似的情况似乎无处不在,无论它们以何种形式出现。现在,我希望能够获取太阳系中的所有国家,同时能够获取如上所示的深度范围的国家。

看来我在这里有两个选择。第一个,我扁平化了我的嵌套结构并引入了很多 GET 参数(需要我的 API 用户详细记录和理解),如下所示:

/countries.json?galaxy=8&solarsystem=5&planet=1&continent=4

我可以像这样展平我所有的资源,并为每个资源赢得一个唯一的端点基础 URL。好点……每个资源的唯一端点!

但是价格是多少?感觉不自然的东西,是不可发现的,并且不像我的资源下面的树结构。结论:坏主意,但实践良好。

另一方面,我可以尝试摆脱尽可能多的额外 GET 参数,创建这样的端点:

/galaxies/8/solarsystems/5/countries.json

但我也需要:

/galaxies/8/solarsystems/5/planets/1/continents/4/countries.json

这似乎是天平的另一面。额外的 GET 参数数量最少,行为更自然,但仍然不是我作为 API 用户所期望的。

去年我使用的大多数 API 都遵循一种或另一种范式。似乎至少有一颗子弹可以咬。那么为什么不做以下事情:

如果存在自然嵌套的资源,让它们完全按照我们期望的嵌套方式嵌套。当我们保持这种状态时,我们首先实现的是每个资源的唯一端点:

/galaxies.json
/galaxies/8/solarsystems.json
/galaxies/8/solarsystems/5/planets.json
/galaxies/8/solarsystems/5/planets/1/continents.json
/galaxies/8/solarsystems/5/planets/1/continents/4/countries.json

好的,但是如何解决最初的问题,我想获取一个太阳系中的所有国家,同时仍然能够获取完全覆盖在星系、太阳系、行星和大陆下的国家?这对我来说很自然:

/galaxies/8/solarsystems/5/planets/0/continents/0/countries.json # give me all countries in the solarsystem 5
/galaxies/8/solarsystems/0/planets/0/continents/0/countries.json # give me all countries in the galaxy 8

……等等,等等。现在你可能会争辩说“好吧,但是那里的零……”你是对的。看起来不是很好。那么为什么不把上面的两个调用改成这样:

/galaxies/8/solarsystems/5/planets/all/continents/all/countries.json # give me all countries in the solarsystem 5
/galaxies/8/solarsystems/all/planets/all/continents/all/countries.json # give me all countries in the galaxy 8

整洁吧?那么我们要达到什么目的呢?每个资源端点没有额外的 GET 参数和仍然稳定的基本 URL。价钱多少?是的,至少 URL 更长,尤其是在使用 curl 等工具进行手动测试时。

我想知道这是否是一种不仅可以提高 API 的可维护性,还可以提高 API 易用性的方法。如果是这样,为什么没有人采取这样的方法。我无法想象成为第一个有这个想法的人。因此,对于这样的方法,必须有有效的反驳论据。我没有看到。你?

我真的很想听听您支持或反对这种方法的意见和论据。也许有改进的想法……很高兴收到您的来信。在我看来,这可能会导致更好的结构化 API,因此希望有人会阅读并回复。

问候。 一月

【问题讨论】:

    标签: ruby-on-rails api http rest


    【解决方案1】:

    这完全取决于数据的呈现方式。用户真的需要知道银河#才能找到特定的国家吗?如果是这样,他们你的建议是有道理的。但是,在我看来,您所提议的内容虽然结构和呈现良好,但不允许客户搜索子元素,除非父元素是已知数量。

    在您的示例中,如果我有一个大陆的特定 ID,我还需要知道行星、太阳系和银河系。为了找到特定的大陆,我需要为每个可能的父级获取 all,直到找到该大陆。

    如果可以的话,以这种方式呈现结构化数据。当你只有一段数据时使用这种结构可能有点麻烦。这完全取决于您要完成的工作。

    【讨论】:

    • 肯,我明白你的意思。大陆的身份不必是唯一的,它们在其范围内(行星、太阳系和星系)变得独一无二,对。在您描述的情况下,我必须像这样请求大陆表示:/galaxies/all/solarsystems/all/planets/all/continents/:id.json。因为我要求一个单一的资源,这至少对我来说是很自然的。事实上,我同意你的看法,这取决于用例。但是您不认为付出的代价(像此评论中的请求一样)值得胜利吗?
    【解决方案2】:

    嵌套的资源 URL 通常是错误的。我通常采用的方法是使用唯一 ID。

    设计您的数据库,使其只有一个 ID 为 4 的大陆。然后,您需要的只是简单的/continents/4/countries.json,而不是可怕的/galaxies/8/solarsystems/5/planets/1/continents/4/countries.json。清晰、充分、令人难忘。

    Rails 中的:shallow 路由选项会自动执行此操作。

    对于“太阳系中的所有国家”,我会使用/solar_systems/5/countries.json——也就是说,不要试图将其硬塞到通用 URL 方案中。 (并注意下划线。)

    【讨论】:

    • Marnen,感谢您的想法。使用唯一 ID 似乎很明显,但并非总是可行。在大多数情况下,由于不同但众多的原因,不需要使用数字 ID。因此,可能会发生 ID 冲突,从而完全展平所有嵌套资源成为一个 noop。此外,如果您的 API 用户希望(必须)更详细地确定返回结果的范围,您会怎么做?
    • 你回来了,同样的痛苦,额外的 GET 参数或相同资源的不同 enpoint 基本 URL。另一方面,您的方法似乎不自然。如果我试图通过 CURL 发现您的 API 服务,我需要阅读大量文档才能理解。考虑到以下帖子(blog.steveklabnik.com/2011/08/07/…blog.steveklabnik.com/2011/07/03/…),我想简化 API 的使用。 Rails3 中的浅路由是我还没有发现的。会这样做并重新考虑。
    • @Jason:“使用唯一 ID 似乎很明显,但并非总是可行。”错误的。给每个对象一个唯一的 ID 总是可能的 - 也是可取的。这对于 DB 设计来说是不言而喻的。如果你不能给每个对象一个唯一的 ID,那么你就没有正确地规范化你的数据模型。 • “在大多数情况下,由于不同但众多的原因,不需要使用数字 ID。”那么 ID 不必是数字。但它们必须是独一无二的。 • “另一方面,你的方法似乎不自然。”它有什么不自然的?对我来说,这似乎非常自然。
    • @Jason:“相同资源的附加 GET 参数或不同的 enpoint 基本 URL。”没有。我的建议是为每个资源提供自己的 URL。 • “如果我试图通过 CURL 发现您的 API 服务,我需要阅读大量文档才能理解。”当然。任何相当复杂的 API 都是如此。除了最简单的数据,我看不出可发现性如何适用于任何其他数据。作为 API 使用者,阅读文档是我的工作。 • “Rails3 中的浅层路由是我尚未发现的。”浅层路由从 Rails 2 开始就出现了,如果不是更早的话。听起来你需要更多学习。
    猜你喜欢
    • 1970-01-01
    • 2011-06-05
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    相关资源
    最近更新 更多