在设计资源层次结构时,何时应该使用子资源?
我曾经认为,当一个资源不能存在而没有另一个资源时,它应该被表示为它的子资源。我最近跑过这个反例:
我将其建模为:/companies/{companyName}/employee/{employeeId}
注意,我不需要查找公司以找到员工,我也应该这样做?如果我这样做,我付出代价来查找我不需要的信息。如果不这样做,则此URL错误地返回HTTP 200:
/companies/{nonExistingName}/employee/{existingId}
答案 0 :(得分:17)
这是有问题的,因为用户所属的不再明显 对某个公司。
有时这可能会突出显示域模型的问题。为什么用户属于公司?如果我改变公司,我是全新的人吗?如果我在两家公司工作怎么办?我是两个不同的人吗?
如果答案是肯定的,那么为什么不采用一些公司唯一标识符来访问用户?
e.g。用户名:
company/foo/user/bar
(其中bar
是我在特定公司名称空间内唯一的用户名)
如果答案是否定的,那么为什么我自己不是用户(人),而company/users
集合只指向我:<link rel="user" uri="/user/1" />
(注意:员工似乎更合适)
现在,在您的具体示例之外,我认为资源 - 子资源关系在使用而不是所有权时更合适(这就是为什么你在挣扎为隐含识别公司的用户识别公司的冗余。
我的意思是users
实际上是公司资源的子资源,因为使用是定义公司与员工之间的关系 - 另一种方式这就是说:在开始雇用员工之前,你必须先定义一家公司。同样,必须先定义(出生)用户(人),然后再招募用户。
答案 1 :(得分:15)
一年后,我以下面的妥协结束(对于包含唯一标识符的数据库行):
/companies/{id}
和/employees/{id}
)。HTTP 307 ("Temporary redirect")
即可。这将导致客户端针对规范URI重复操作。/companies
但不是/employees
。这种方法有以下好处:
/companies/{companyId}/employees/{employeeId}/computers/{computerId}
进行两次验证检查。答案 2 :(得分:6)
您决定是否应将资源建模为子资源的规则是有效的。您的问题不是来自错误的概念模型,而是让您的数据库模型泄漏到REST模型中。
从概念角度看,如果employee
只能存在于company
关系中,则employee
被建模为composition。因此company
只能通过employee
识别。现在数据库发挥作用,所有when a resource could not exist without another, it should be represented as its sub-resource
行都获得唯一标识符。
我的建议是不要让您的概念模型中的数据库模型泄露,因为您将基础架构问题暴露给您的API。例如,当您决定切换到像MongoDB这样的面向文档的数据库时会发生什么情况,您可以将员工作为公司文档的一部分进行建模,而不再具有这种人为的唯一ID?您想要更改API吗?
回答你的额外问题
我应该如何表示属于另一个资源的事实?
通过子资源组合,通过URL链接进行其他关联。
如果没有其他资源,我该如何表示无法识别资源?
在资源网址中使用这两个id值,并确保通过检查“组合”是否存在,不要让数据库泄漏到您的API中。
子资源意味着什么关系,而不是模型?
子资源非常适合于合成,但更一般地说是为了模拟资源在没有父资源的情况下不能存在并始终属于一个父资源。您的规则{{1}}是此决定的良好指导。
答案 3 :(得分:4)
如果子资源在没有其拥有实体的情况下是唯一可识别的,则它不是子资源,并且应该具有自己的命名空间(即/ users / {user}而不是/ companies / {*} / users / {user})。 最重要的是:永远不要使用您实体的数据库主键作为资源标识符。这是实施细节泄露给外界的最常见错误。您应始终拥有自然的业务密钥(例如用户名或公司号,而不是用户ID或公司ID)。如果您愿意,可以通过唯一约束来强制执行此类密钥的唯一性,但实体的主键永远不应该永远留下应用程序的持久层,或者至少它永远不应该是任何服务的参数方法。如果按照此规则进行操作,则不应该在组合(/ companies / {company} / users / {user})和关联(/ users / {user})之间进行区分,因为如果您的子资源没有&# 39;有一个自然的业务密钥,在全局环境中标识它,你可以非常肯定它确实是一个依赖的子资源(或者你必须首先创建一个业务密钥才能使其全局可识别)。
答案 4 :(得分:1)
这是解决这种情况的一种方法:
/ companies / {companyName} / employee / {employeeId} - &gt;返回有关员工的数据,还应包括该人员的数据
/ person / {peopleId} - &gt;返回有关此人的数据
谈论员工在没有谈论公司的情况下没有任何意义,但即使没有公司,即使他被多家公司聘用,谈论这个人确实有意义。一个人的存在与他是否被任何公司雇用无关,但就业的存在确实取决于公司。
答案 5 :(得分:0)
问题似乎是当没有特定公司但员工在技术上属于某个公司或组织时,否则他们可能被称为流浪汉或政治家。作为一名员工意味着某处的公司/组织关系,而不是特定的关系。员工也可以为多个公司/组织工作。如果需要特定的公司上下文,则原始作品/companies/{companyName}/users/{id}
假设您想知道您使用的ira / rsp /养老金的EmployerContribution
:
/companies/enron/users/fred/EmployerContribution
您将获得由enron(或$ 0)贡献的特定金额。
如果您希望来自任何或所有公司的EmployerContribution
fred工作(编辑)怎么办?
您不需要具体公司才能理解。 /companies/any/employee/fred/EmployerContribution
当员工的公司无关紧要而且是员工的情况时,“任何”显然是抽象或占位符。您需要拦截“公司”处理程序以防止数据库查找(虽然不确定为什么公司不会被缓存?有多少可以?)
您甚至可以将抽象更改为代表过去10年Fred所聘用的所有公司。
/companies/last10years/employee/fred/EmployerContribution