在 Restful API 中创建多对多关系的“正确”方法是什么?

4

我试图找到最佳实践来创建Restful API中的多对多关系。这个用例非常简单,但是我不能真正找到“正确”的方法。

在我们的模型中,我们有与Guardian存在多对多关系的Kid。在关系表中,我们有两个额外的参数:类型(父母,保姆,紧急情况等)和活动(布尔值)。

您只能将Guardian添加到现有的Kid中,但是现有的Guardian可以链接到另一个Kid

今天,我们就是这样做的

POST kids/{kidID}/guardians
{
    "type": "parent"
    "active": false 
    "guardian": {
        "first_name": "foo"
        "last_name": "bar"
    }
}

这将创建一个Guardian并将其添加到孩子中。但是,使用这种方法,我们无法处理我想要将现有的Guardian添加到Kid的情况。以下是我找到的解决方案,但我不知道哪个是最好的(也许没有一个好...):

解决方案1 - 保持今天的端点

但是在guardian中放置一个非强制性的id字段。如果id为空,则API必须创建资源;否则,只需检索它并在需要时更新其值。

POST kids/{kidID}/guardians/
{
    "type": "parent"
    "active": false 
    "guardian": {
        "id": "ab65f263-dd3d-bbc6-8b7b-57a3b4b26c21"
    }
}

解决方案2 - 将此端点拆分为两个调用

# Create the Guardian
POST guardians/
{
    "first_name": "foo"
    "last_name": "bar"
}

# This method can only "link" the models
POST kids/{kidID}/guardians/
{
    "type": "parent"
    "active": false 
    "guardian_id": "ab65f263-dd3d-bbc6-8b7b-57a3b4b26c21"
}

[编辑] 解决方案 2.5 - 使用PUT创建关系

与之前一样,您需要创建监护人,但是为了添加关系,您需要执行PUT操作。

PUT kids/{kidID}/guardians/{guardianID}
{
    "type": "parent"
    "active": false 
}

子方案:在第二个选择中,我们可以通过更改资源的URI来解决问题:

POST kids/{kidID}/kid-guardians/

因为它实际上没有发布“guardian”资源,而是发布了一个孩子-监护人的资源(关系)。我不太喜欢这个,因为使用旧的URI,我们可以更容易地假设

GET kids/{kidID}/guardians/

我会为你提供与孩子相关的所有Guardians(监护人),但不包括其他方面。

DELETE kids/{kidID}/guardians/{guardianID}

将删除关联,但不删除Guardian(监护人)

所以,您可以理解我确实很迷茫,希望您的帮助。

最好的问候,

2个回答

2
无法为关系本身创建第三类资源,例如“guard”,它不隶属于其他资源的实例吗?在数据库中处理n对n关系的推荐和通常方式似乎是这样做。 GET /guards?kid="Johnny"将给您一个关系列表,您可以使用它来获取所有监护人。 GET /guards?guard="Kelly",您可以猜到。 /kids/guards仅保留有关资源本身的数据,与将关系数据作为其一部分保留相比,可能更容易维护。
我认为,通过使用链接到每个关系成员而不是数字ID,您可以使此更具RESTful性质。并且您可以在孩子和监护人表示中拥有“关系”字段,其中包含检索其特定“guards”所需的URL +查询字符串,如果需要,某人可以使用它们。

谢谢你的回答。我没有采用这种方法,有几个原因: 1- 对我来说,它更像是数据库思维而不是REST。 2- guards EP 真的是一个资源吗?所有CRUD对我来说似乎都没有用,我真的需要GET guards/{guardID}吗? 3- 我们能不能使用你的EP kids/{kidID}/guardians/ 来检索关系。再次感谢您的帮助,也许这是最好的解决方案,我的RESTful观点完全错误。 - slaynerM
1 - 我对数据库和REST都不是专家,但看起来这似乎是一个建模问题,如果关系在业务模型中很重要,那么它确实是API中的一个潜在资源。
2 - 如果您想获得有关特定关系的数据,并且这些关系可以随时间而来去,则GET/POST/PUT/DELETE /guards/{id}是有意义的,似乎更容易维护,甚至可以在客户端应用程序中隐藏,在您的#3想法(下一步)之后。
3 - 您可以创建这样的别名,当然也可以使用/guardians/{id}/kids。顺便说一下,我不知道"EP"是什么意思:)祝好运!
- Fabricio Rocha

0

我会采用Fabricio Rocha的答案,实现方式如下:

POST guardian-kids/
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}"
    },
    "kid":{
        "id": "{kidId}"
    }
}

如果你想要检索监护人-孩子

GET guardian-kids/{GuardianKidId}
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

我也创建了这两个端点(你只能在这些端点上进行GET请求)

GET kids/{kidId}/guardian-kids
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

GET guardians/{guardianId}/guardian-kids
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

我在其他方法中看到的“问题”是,/kids/{kidID}/guardians/和/guardians/不会代表相同类型的资源,但具有相同的名称。

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