如何在REST超媒体API中构建交集?

时间:2015-10-02 22:33:01

标签: api rest hateoas hypermedia

这个问题与语言无关。让我们不要担心框架或实现,让我们说一切都可以实现,让我们以抽象的方式看待REST API。换句话说:我现在正在构建一个框架,而且我在任何地方都没有看到任何解决这个问题的方法。

问题

如何为两个返回集合的独立REST路径的交集构建REST URL端点?简短示例:如何交叉/users/1/comments/companies/6/comments

约束

所有端点都应返回单个数据模型实体或实体集合。

Imho这是一个非常合理的约束,所有超媒体API的例子都是这样的,即使在draft-kelly-json-hal-07中也是如此。

如果您认为这是一个无效的约束,或者您知道更好的方法,请告诉我。

示例

因此,我们假设我们的应用程序有三种数据类型:productscategoriescompanies。每家公司都可以在其个人资料页面中添加一些产品添加产品时,必须在产品中附加类别。例如,我们可以访问这样的数据:

  • GET /categories将返回所有类别的集合
  • GET /categories/9将返回id 9
  • 的类别
  • GET /categories/9/products将返回id 9
  • 类别中的所有产品
  • GET /companies/7/products将返回添加到id 7
  • 公司的个人资料页面的所有产品

我故意省略_links超媒体部分,因为它很简单,例如/_links提供给/categories和{{1}我们只需要记住,通过使用超媒体,我们遍历关系图。

如何编写将返回的网址:所有来自公司(7)且属于类别(9)的产品?换句话说,如何交叉/companies/categories/9/products

假设所有端点都应该代表数据模型资源或它们的集合我相信这是REST超媒体API的一个基本问题,因为在遍历超媒体api时,我们遍历一条路径的关系图,因此不可能描述这样的交集因为它是两个独立图形路径的横截面。

换句话说,我认为我们不能代表只有一条路径的两条独立路径。通常我们遍历一条路径,例如/companies/7/products,但如果我们有A->B->CX->Y,我们希望所有来自Z->YY的{​​{1}}那我们就有问题了。

到目前为止,我的主张是使用查询字符串:X但我们可以做得更好吗?

为什么我要这个?

因为我正构建一个框架,它将根据SQL数据库关系自动生成REST Hypermedia API。您可以将其视为Z查询的URL的转换编译器,但API的客户端只能看到超媒体,并且客户端希望有一种很好的交叉方式,如示例中所示。

4 个答案:

答案 0 :(得分:2)

我认为你不应该总是将REST视为数据库表示,这种情况对我来说更像是一种特定的功能。我想我会选择这样的东西:

/intersection/comments?company=9&product=5

我写完之后就一直在挖掘,这就是我发现的(http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api):

  

有时候你真的无法将动作映射到合理的RESTful结构。例如,多资源搜索实际上没有意义应用于特定资源的端点。在这种情况下,即使它不是资源,/ search也会最有意义。这没关系 - 只需从API使用者的角度做正确的事情,并确保明确记录以避免混淆。

答案 1 :(得分:1)

您要做的是过滤其中一个类别中的产品......如果我们有以下内容,请按照您的示例进行操作:

GET /categories/9/products

以上将返回第9类中的所有产品,因此要过滤掉公司7的产品,我会使用类似的产品

GET /categories/9/products?company=7

您应该将URI视为获取所有数据的链接(就像SQL中的简单选择查询一样),并将查询参数视为where,limit,desc等。 使用此方法您可以构建复杂且可读的查询。

GET /categories/9/products?company=7&order=name,asc&offset=10&limit=20

答案 2 :(得分:1)

  

所有端点都应返回单个数据模型实体或集合   实体。

这不是REST约束。如果您想了解REST约束,请阅读Fielding dissertation

  

因为我正在构建一个自动生成REST的框架   基于SQL数据库关系的超媒体API。

这是一种错误的方法,与REST无关。

通过REST,您可以通过在响应中发送超链接来描述可能的资源状态转换(或操作调用模板)。如果您使用HTTP和URI标准构建统一接口,这些超链接由HTTP方法和URI(以及其他现在不相关的数据)组成,我们通常会这样做。 URI不是(必然)数据库实体和集合标识符,如果应用这样的约束,最终将使用CRUD API,而不是REST API。

如果您无法使用HTTP方法和现有资源的组合来描述操作,那么您需要一个新资源。

在您的情况下,您希望汇总GET /users/1/commentsGET /companies/6/comments个回复,因此您需要定义与GET和第三个资源的链接:

GET /comments/?users=1&companies=6
GET /intersection/users:1/companies:6/comments
GET /intersection/users/1/companies/6/comments

等...

答案 3 :(得分:1)

RESTful架构是关于返回包含提供状态转换的超媒体控件的资源。我在这里看到的是状态转换的多步骤过程。假设您有一个根资源,并使用可用的超媒体控件以某种方式导航到/categories/9/products。我敢打赌结果看起来像是这样的:

{
  _links : {
     self : { href : "/categories/9/products"}
  },
  _embedded : {
     item : [
        {json of prod 1},
        {json of prod 2}
     ]
  }
}

如果您希望客户端能够将其与另一个集合交叉,则需要向他们提供执行此操作的机制。你必须给他们一个超媒体控制。 HAL只有链接,模板化链接和嵌入式控件类型。让我们使用链接..将响应更改为:

{
  _links : {
     self : { href : "/categories/9/products"},
     x:intersect-with : [
          { 
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 1",
            title : "Company 6 products"
          },
          {
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 2",
            title : "Company 5 products"
          },
          {
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 3",
            title : "Company 7 products"
          }
     ]
  },
  _embedded : {
     item : [
        {json of prod 1},
        {json of prod 2}
     ]
  }
}

现在,客户端只根据链接的标题字段选择正确的超媒体控件(也就是链接)。

这是最简单的解决方案。但是你可能会说1000多家公司我不想要1000个链接......好吧,如果那样的话,那真的是这样......你呢只是在我们的两个中间提供一个状态转换:

{
  _links : {
     self : { href : "/categories/9/products"},
     x:intersect-options : { href : "URL to a Paged collection of all intersect options"}, 
     x:intersect-with : [
          { 
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 1",
            title : "Company 6 products"
          },
          {
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 2",
            title : "Company 5 products"
          },
          {
            href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 3",
            title : "Company 7 products"
          }
     ]
  },
  _embedded : {
     item : [
        {json of prod 1},
        {json of prod 2}
     ]
  }
}

看看我在那里做了什么?额外的状态转换的额外控制。只要你有一个网页,就像你会做的那样。你可能会把它放在弹出窗口中,以及你的应用程序的客户端也可以使用该控件的结果。

真的那么简单......只要想想你是如何用HTML做的并做同样的事情。

这里的一大好处是,客户永远不需要知道公司或类别ID,或者将其插入某个模板。 id是实现细节,客户端永远不知道它们存在,它们只是执行了超媒体控件......而且是RESTful。