定义 REST api

Defining a REST api

我想知道定义休止符的最正确或最佳实践方法是什么 api。假设我有一个处理汽车及其 parts/components 的应用程序。任何 api 总是首先从顶级汽车开始。因此,在定义我的 api 时,我应该使用:

[url]/{resourceName}/{carId}/{resouorceId}

举个例子,假设我想要一个引擎,所以:

[url]/engine/123/456

其中 123 是汽车 ID,456 是引擎 ID。或者,将资源放在顶级容器之后是更好的做法,在本例中是汽车,例如:

[url]/{carId}/{resourceName}/{resourceId}

所以同样的例子是:

[url]/123/engine/456

或者这重要吗?是否有 "industry best practice" 类型的方法?据我了解,我读到的大部分内容都表达了 "make the api intuitive and easy to understand".

的意思

此外,api 是否或应该具有充分的表现力,例如,所有 api 是否应该像下面的方法一样,即使 api 总是用于用于此特定应用的汽车。

[url]/car/{carId}/{resourceName}/{resourceId}

所以,例子是:

[url]/car/123/engine/456
[url]/car/123/wheel/1

我知道这可能会被解释为 post 只会引起争论,但我真的在寻求帮助以找出可能被最广泛接受的内容。

REST 确实与 URI 或 HTTP 无关。这不是一个标准;它是一种架构 风格 范式 。在 Roy Fielding 的论文中,他确定了建筑风格的某些标准 "RESTful"。恰好 HTTP 满足这些条件,这使得它成为一个很好的 协议 来实现 RESTful 架构。但是 REST 本身与 HTTP 没有任何关系

在 REST URI 中是不透明的,这意味着它们除了唯一标识资源外不传达任何其他语义。例如,像 /dfjSuX7kx 这样的 URI 是完全有效的。当然,这并不意味着我们必须像那样使用 URI。最好使用你能理解的东西。

您想知道是否应该选择:

[url]/{resourceName}/{carId}/{resourceId}

我会说最好只选择:

[url]/{resourceName}/{resourceId}

然后将carId作为表示的一部分。这是为什么?我们知道其中一项要求是您的 URI 必须 唯一地 标识资源。现在我们有一个引擎 456,它是汽车 123 的一部分,这意味着您正在使用 URI /engine/123/456 标识该特定资源。但是假设汽车 123 被总计,引擎被转移到汽车 789 上。现在 same 资源的 URI 是 /engine/789/456。请记住,关于引擎 456 没有任何变化;它仍然是 相同的 引擎。唯一的问题是它现在连接到另一辆车上。但这并不意味着唯一标识它的 URI 也必须更改(因为那样它将不再 唯一地 标识它)。 唯一标识引擎的是它的id,不是它所连接的汽车。因此最好只使用 /engine/456 和 return 这样的表示:

{
    "id": 456,
    "carId": 123,
    "cylinders": 6,
    "hp": 350,
    ...
}

你也问过你是否可以这样做:

[url]/{carId}/{resourceName}/{resourceId}

我根本不推荐这样做。假设您在 http://my.car-api.com 托管了 API。 http://my.car-api.com/123 到底是什么意思?当然是RESTful,因为URI是不透明的,但是看URI的时候很难理解是什么意思,所以还是用/car/{carId}.

比较好

对于最后一个问题,您问的是以下是否可以接受:

[url]/car/{carId}/{resourceName}/{resourceId}

子资源还可以,我看到有人用它,我也在我创建的一些 REST API 中使用它。但就我个人而言,我正在努力为每个资源提供一个顶级 URI。这是因为我们已经可以通过我们从 URI return 的表示来传达层次关系或 "ownership" 的概念,而不是将其编码到 URI 本身(请记住,URI 除了除了它唯一标识资源的事实)。我还注意到在我实现控制器、错误处理程序和转换器(将 DTO 转换为域对象的助手 类,反之亦然)的方式中存在正交性问题;支持嵌套的 URI 结构使我很难编写通用代码(尽管其中一些是由于我使用的框架 Spring 做事的方式)。

您可以决定 "RESTful" 您想要成为什么样的人;我见过的用于量化 API 的 RESTfulness 的最佳指标是 Richardson Maturity Model。我建议你决定一个你觉得舒服的水平。没必要完全教条,完全RESTful;这取决于你的情况。但我发现达到级别 3 (HATEOAS) 给我带来了很多好处,因为我的自定义媒体类型(即我使用 application/vnd.my.api.resource.v1+json 而不仅仅是 application/json)有据可查并传达了他们的语义很好。另外 none 我的客户必须构建 URI,因为他们只能跟踪链接。这些表示也是自文档化的,因为您可以点击相关文档的链接,以获取有关处理资源的说明。

我唯一 强烈建议 的建议是选择一种风格并坚持下去。我不喜欢混合范式;因此,如果您的目标是 RESTful,请不要让任何类似 RPC 的东西潜入您的 API。这可能有点困难,因为 REST 是基于名词的,而 RPC 是基于动词的(即行为),并且一些概念很容易映射到 RPC。一个例子是,如果您有一家银行 API,您希望提供从一个帐户向另一个帐户转帐的功能。一个真正的 RPC-ish 的 "REST" 实现可能会公开一个像 /account/{accountId}/transfer?to={toAccountId}&amount={amount} 这样的 URI(它直接映射到 Account 对象上的方法调用:account.transfer(amount, toAccountId);因此是 RPC)。 RESTful 方法是在 /transaction 处公开一个 transaction 资源,你会 post 像这样:

{
    "from_account_id": 123,
    "to_account_id": 456,
    "amount": "100.00"
}

并且 /transaction 可以响应:

{
    "id": 789,
    "source_account_id": 123,
    "destination_account_id": 456,
    "amount": "100.00",
    "_links": {
        "self": { "href": "/transaction/789" }
    }
}

很多时候人们最终会复制 HTTP 已经通过 RPC 调用提供的功能。例如,HTTP 已经为您提供了转换为 CRUD 操作的方法(快速注意:方法本身与 CRUD 语义无关;它们只是碰巧方便地映射),并且还具有传达有关最终结果信息的响应代码操作(成功了吗?失败了吗?为什么失败了?等等)。 REST 的想法是,您可以专注于对资源、它们的表示以及它们之间的关系进行建模,并使用 HTTP 已经为您提供的(超媒体、响应代码、方法),而不是复制所有这些。