API应该消耗自身还是直接调用数据库获取其他资源?

5

这个问题是之前发布的这个问题的扩展,尽管是分开的。

因此,我们有一个基本的车库示例,其中有多辆汽车,因此我们的端点是

/garages
/garages/{id}
/garages/{id}/cars
/garages/{id}/cars/{id}
/cars
/cars/{id}

我们可以使用/cars?garage[id]=1,2,3从多个车库中获取所有汽车,这很酷。但我现在想知道的是API内部的情况。
有两种方法可以做到这一点:

在/cars端点中直接过滤

这意味着我们在查询汽车时进行连接并添加一些条件。
这种方法的好处是我们最终会得到最少量的查询结果。这种方法的缺点是我们要在两个地方维护车库资源。每当车库获得新属性时,我们现在也必须在汽车端点中支持此属性。

从/cars端点调用/garages端点

这意味着我们从/cars端点内部调用/garages端点,/garages返回与匹配车库中所有汽车的ID。然后我们继续从/cars端点获取汽车。
这种方法的好处是资源是自包含的。这种方法的缺点是我们将最终得到多次对数据库的调用。此外,传递身份验证详细信息可能会变得麻烦(假设Oauth 2.0)。
那么哪种方法最合适?我倾向于第二种方法,但我担心如果我们想要进行更高级的查询,这可能会变得非常麻烦。
2个回答

3

使用自己的端点(这被称为服务分层,具有许多好处),但不要与您的端点紧密耦合。

首先,我不会按数字ID查询,因为这与您的实现紧密绑定。如果您将URL用作车库标识符,则将获得更多的灵活性,并且可以轻松地添加对其他系统中的车库的支持。

使用超媒体控件(即链接和表单)在汽车列表上添加搜索表单。假设您的汽车列表看起来像这样(为简洁起见,排除了方案和主机)

<cars self="/cars">
    <car href="/cars/0"/>
    <car href="/cars/1"/>
    ...
</cars>

注意:您需要为汽车添加一些摘要属性(例如注册、制造商、型号等),以使搜索有意义,而不必返回完整的汽车实体。

要添加搜索功能,我们可以添加类似于

<cars self="/cars">
    <car href="/cars/0"/>
    <car href="/cars/1"/>
    ...
    <form name="search" action="/cars" method="get">
        <input name="garage" type="URL"/>
        <!-- other things to search for can go here -->
    </form>
</cars>

在您的资源库(例如数据库)中,如果您的汽车存储了它们所在车库的URL,则可以执行此查询而无需查询任何车库,并且如上所述,支持在完全不同的系统中拥有车库,假设另一个系统使用您的媒体类型或您支持其媒体类型。
显然,在搜索时您需要访问车库的URL,这可以通过进行车库搜索来完成。例如:
<garages self="/garages">
    <garage href="/garage/0"/>
    <garage href="/garage/1"/>
    ...
    <form name="search" action="/garages" method="get">
        <input name="paint" type="string"/>
        <!-- other things to search for can go here -->
    </form>
</garages>

所以,当用户想在多个车库中搜索汽车时,他们首先搜索那些车库,并将感兴趣的车库添加到列表中(这只是一组URL列表)。然后使用车库列表作为输入搜索汽车。
在这种情况下,车库和汽车仅由URL耦合。
您可以通过在车库集合中添加指向汽车搜索的链接来进一步扩展此功能。例如,假设我们搜索了黄色涂漆的车库,我们可能会得到一个像这样的集合。
<garages self="/garages?paint=yellow">
    <garage href="/garage/24"/>
    <garage href="/garage/36"/>
    ...
</garages>

为了获取此集合的汽车列表,我们可以添加以下链接。
<garages self="/garages?paint=yellow">
    <garage href="/garage/24"/>
    <garage href="/garage/36"/>
    <link rel="cars" href="/cars?garage=/garage/24,/garage/36"/>
</garages>

这个方法对于小型的车库列表是可行的,但当车库列表增长时会变得麻烦,此时汽车的URL将变得过长。相反,我们可以使用搜索的URL作为输入参数。例如:
<garages self="/garages?paint=yellow">
    <garage href="/garage/24"/>
    <garage href="/garage/36"/>
    <link rel="cars" href="/cars?garages=/garages?paint=yellow"/>
</garages>

在这种情况下,当您跟随汽车链接时,您的服务将需要执行车库查询,检索每个车库的汽车列表,然后返回合并的汽车列表。除了更短的URL之外,此查询的好处是它始终会为您提供带有黄色油漆的车库中的汽车列表,即使车库的油漆库存发生变化。
您的服务如何获取车库中的汽车列表?车库实体可以有一个指向汽车集合的链接,例如。
<garage self="/garage/24">
    ... details about the garage ...
    <link rel="cars" href="/cars?garage=/garages/24"/>
</garages>

正如我在开头所说,使用您自己的终端节点,但不要通过假设它们是您自己的终端节点而紧密耦合它们。为实体构建超媒体控件,当您的服务需要使用它们时,将其视为任何其他外部API。

最后(略微偏题),为了支持大量汽车和车库的集合,您可以向您的集合添加分页功能,例如:

<garages self="/garages?page=2">
    <garage href="/garage/10"/>
    <garage href="/garage/11"/>
    ...
    <link rel="next" href="/garages?page=3"/>
    <link rel="prev" href="/garages"/>
</garages>

你甚至可能希望让你的集合符合RFC5005 Paged FeedsComplete Feeds,以便可以使用标准工具消耗它们。
更新:
以下是一个带有超媒体控制的JSON车库集合示例,使用了上述一些概念:
{
    "self": "/garages?paint=yellow&page=2",
    "garages": [
        {
            "href": "/garage/24"
            //... summary properties for this garage go here ...
            //... you can even add a "media-types" array, to tell the service consumer
            //    what media types the garage is available in... 
        },
        {
            "href": "/garage/36"
        }
    ],
    "next": {
        "href": "/garages?paint=yellow"
    },
    "prev": {
        "href": "/garages?paint=yellow&page=3"
    },
    "cars": {
        "href": "/cars?garages=/garages?paint=yellow"
    },
    "search": {
        "href": "/garages?paint=yellow",
        "method": "GET",
        "inputs": {
            ... form input parameters go here ...
        }
    }
}

1
超媒体控件是否总是在XML中指定?如果您的API是面向JSON的,那么上述示例会是什么样子? - Hailwood
@Hailwood,它们不一定要是XML格式,可以是JSON或YAML或您选择的任何格式。我会添加一个示例。 - Tom Howard

0

好消息是它在幕后进行,所以如果出现错误,您可以稍后更改实现。我肯定也会选择#2。在不知道是否存在性能问题之前试图摆脱数据库调用太像过早优化了。这取决于相关调用的频率以及您需要系统有多高的性能。

传递oauth令牌可能很烦人,但似乎比在多个位置维护资源的风险要小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接