我可以在HATEOAS中使用相同的URI建立多个链接关系吗?

时间:2015-07-08 12:18:00

标签: hateoas

我有一个资源,在这种情况下:

http://www.domain.com/sales-rep/{id}

每个销售代表都有多个可以销售的产品。此资源的URI为:

http://www.domain.com/sales-rep/{id}/products

问题在于,根据用户的权限,您可能只能对此资源GETGETPUT

我的问题是在检索销售代表资源时设计链接关系。例如,我的JSON可能如下所示:

{ 
  firstname : "Dave",
  lastname : "Matthews",
  links : [ 
    { rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
    { rel : "products", href : "http://www.domain.com/sales-rep/dmat1/products" }
  ] 
}

如果采用这种方法,我的链接关系可以很好地命名为代表资源的名词。但是,它无法与客户端通信是否可以编辑资源。据我所知,链接中的HTTP方法不属于规范&这些是文档的责任,所以我如何传达权限?

    { 
      firstname : "Dave",
      lastname : "Matthews",
      links : [ 
        { rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
        { rel : "view-products", href : "http://www.domain.com/sales-rep/dmat1/products",
        { rel : "edit-products", href : "http://www.domain.com/sales-rep/dmat1/products" }
      ] 
    }

这样的东西可以起作用,使客户可以根据edit-products rel的存在来创建具有正确链接的菜单。然后,两个不同rels的curies将链接到文档中的不同部分,分别用于GETPUT。但这有意义吗?这两种方法都让我觉得不对。

1 个答案:

答案 0 :(得分:2)

简短回答是肯定的,不同链接背后的网址经常出现。但你担心的并不是简单的答案,而是完全基于你想要的用户体验。

请注意查看产品&编辑产品不是注册链接关系(请参阅http://www.iana.org/assignments/link-relations/link-relations.xhtml)列表,因此它们实际上应该是URI。

这很重要,因为让这些URI可以解除引用/因此实际包含链接关系含义的文档的网址非常普遍。因此,您的编辑产品变成了一个URL,例如http://example.com/rels/edit-products,并且检索到的结果可能会生成指示可编辑内容的文档。

但我会发现这个级别的编辑链接相当奇怪。通常,您可以在此处编辑一些内容,产品集合以及集合中的各个产品。让我们首先关注后者。

每种产品都可能是一种资源,因此它拥有自己的自我链接。如果我想以开发人员的身份更改该产品,我可以自然地将代表产品的一些JSON用于自我链接。我不需要编辑链接,因为我的意图很清楚我的要求。如果我无法编辑,那么我应该得到一个回复​​来表明这一点(以及它是否通过http相应的http响应代码)。如果我想删除该产品,我可以发送DELETE到自我链接的URL。如果我想特别修改产品,我可能会使用PATCH请求。如果您想要更多限制的编辑功能,那么编辑形式的IANA链接关系将是一个很好的候选者,在此之后将返回用于编辑资源的表单,如服务打算/允许的那样。再次,您可以拥有自己的URI关系,遵循您记录的任何约定。您似乎有的另一个考虑因素是您希望客户端知道它是否可以编辑资源。如果您执行编辑表单案例,那么就像拥有或不存在该关系一样简单。您还可以使用编辑IANA关系来指示原始编辑功能(并且它可以与自己具有相同的URL)。

所以我认为这涵盖了产品,让我们谈谈这个系列。

即使您已经确定每种产品都不是自己的资源,编辑收藏品的想法也是一样的。将产品添加到集合的自我URL中意味着将产品添加到集合中,而将产品集合的PUT添加到自我URL将替换整个集合。 PATCH会修改集合。最重要的是,如果您有一些有趣的案例,用户只能添加产品,但不能删除或重新排序,那么我将开始深入了解自定义链接关系。如果您需要这样的功能,关系的存在可以再次向客户表明可以做什么和不可以做什么。

我可能会质疑为什么你有一个产品收集资源。相反,为什么不从销售代表那里获得产品链接:

{ 
  firstname : "Dave",
  lastname : "Matthews",
  links : [ 
    { rel : "self", href : "http://www.domain.com/sales-rep/dmat1" },
    { rel : "product", href : "http://www.domain.com/product1" },
    { rel : "product", href : "http://www.domain.com/product2" },
    { rel : "product", href : "http://www.domain.com/product3" },
    { rel : "product", href : "http://www.domain.com/product4" }
  ] 
}

我再也不知道您的整个模型和用户体验,但这可以让您编辑销售代表资源以添加或删除产品,而产品则独立作为自己的资源。

一些不太清楚的事情是,您是否希望编辑产品返回用于编辑产品的资源(如表单)。我经常觉得考虑我的JSON api非常有用,就像我的HTML应用程序(网站)一样,因为网站本质上非常REST。在HTML驱动的应用程序中,情况可能就是如此。在销售代表页面(资源)上的IE,您有一个指向允许用户编辑其产品的页面(资源)的链接。同上查看产品。在这种情况下,这些视图产品和编辑产品页面可能是相同的资源(IE它是允许您查看和编辑的同一页面),或不同的资源(IE两个不同的页面只用于编辑,一个仅用于观看)。在前者中,他们可能具有相同的URL,而在后者中他们具有不同的URL。 RESTful的优点在于,您的应用程序可以在不更改语义的情况下进行更改。首先,您可能希望它们是单独的页面,一个用于查看一个用于编辑,因此它们是不同的URL,但是您可能会改变主意并决定一个页面来执行这两个操作是合适的。现在,两个链接关系具有相同的URL,但是在这些关系之上构建的任何UI仍然可以正常运行并将用户转换为所需的资源。

还有另一个选项(双关语),您可以向自己的网址提交OPTIONS请求。这应该为请求用户提供http方法,例如PATCH,PUT,POST,DELETE等,从这个选项集合中,您可以构建提供正确UI控件的UI。遗憾的是,这需要额外的往返服务器,但是确定当前客户端允许的功能是一种非常好的机制。

有了这个,我提出了最重要的一点。一般来说,请求方法(以及整个请求)意味着客户端的意图(更新,删除,添加,重置,补丁等),服务应解释该意图(以及记录可能的意图)并决定是否满足要求。使用这种方法和OPTIONS请求,除了SELF链接之外,通常不需要为简单的CRUD应用程序提供层次关系(例如:产品)