我有一个返回单元素集合的端点(我不只是返回对象实例以保持与资源即集合约定的一致性,所以只返回一个实例)
GET /devices?serialNumber=12345
[
{
"id": 1,
"serialNumber": "12345"
}
]
新的需求出现了:一些设备成对连接,在搜索与设备12345
配对的设备78901
时,我需要检索这两种设备(最好是在单个HTTP调用中)。我最好的选择是什么?我试过:
GET /devices?serialNumber=12345
[
{
"id": 1,
"serialNumber": "12345"
},
{
"id": 2,
"serialNumber": "78901"
}
]
但这打破了语义(一个S/N的I过滤器设备资源“列表”,突然出现另一个具有不同S/N的设备)
然后我试了一下:
GET /devices?serialNumber=12345
[
{
"id": 1,
"serialNumber": "12345"
"connected":
{
"id": 2,
"serialNumber": "78901"
}
}
]
但是递归嵌套资源仍然感觉不太好,因为这与域模型的表达方式完全不同。是否有更好的方法来设计这个端点而不公开它们如何连接的细节?有一些业务逻辑非常复杂,与端点使用者无关,她只需要知道是否有配对的设备。
发布于 2017-01-23 05:58:23
但是递归嵌套资源仍然感觉不太好,因为这与域模型的表达方式完全不同。
没有充分的理由将您的API资源表示形式强烈地耦合到域模型中。他们以不同的需求为不同的客户服务,他们的设计模式也完全不同。
我建议您添加对查询参数?includeConnectedDevices=true
的支持。那你就可以
GET /devices?serialNumber=12345
[
{
"id": 1,
"serialNumber": "12345"
}
]
GET /devices?serialNumber=12345&includeConnectedDevices=true
[
{
"id": 1,
"serialNumber": "12345",
"connected":
{
"id": 2,
"serialNumber": "78901"
}
}
]
客户可以选择是否关心连接的设备(S)。作为一种奖励,您可以使用任意数量的连接。
不管你做什么,你都应该考虑包括一个或多个链接,从一个设备到它的连接设备(S)。这可能在标题中,或者作为实体的属性,或者您将响应包装在一个信封中。下面的链接比构建URL要好得多。那看起来可能像/devices?connectedTo=12345
。
发布于 2017-01-23 06:00:08
您可以使用哈特奥链接您的资源,因此不必返回以下内容:
GET /devices?serialNumber=12345
[
{
"id": 1,
"serialNumber": "12345"
},
{
"id": 2,
"serialNumber": "78901"
}
]
您可以返回如下内容:
GET /devices?serialNumber=12345
{
"id": 1,
"serialNumber": "12345",
"links":[
{
rel:"connected",
"id":2,
"serialNumber": "78901"
}
]
}
@Ewan是完全正确的,应该是这样的:
GET /devices?serialNumber=12345
{
"id": 1,
"serialNumber": "12345",
"links":[
{
"rel":"connected",
"href":"http://yourserver.com/yourapp/devices?serialNumber=78901"
}
]
}
**第二次更新**(在喝了我早上的咖啡后)
如果您查看这些注释,您会发现,虽然最初的答案符合您的所有要求(我赞同@Eric关于“这不需要一个HTTP请求”),但是第一个更新是“标准”(至少其中一种)方法。IMHO您应该使用HATEOAS或HAL,或者其他一些众所周知的替代方法,而不是执行嵌套资源的操作。
发布于 2017-01-23 19:54:24
出现了新的要求:一些设备成对连接,在搜索设备12345 (与设备78901配对时)时,我需要检索这两个设备(最好是在单个HTTP调用中)。我最好的选择是什么?
这将取决于你的约束。
有一种方法非常简单:认识到新的需求描述了设备12345的新视图。也就是说,它描述了一个新文档,这是描述新资源的另一种方式。
因此,为这个新报告找出一个名称,然后将该名称与您的URI拼写标准一起使用,以找到一种合理的方法。
GET /devicePairs?eitherDevice=12345 ...
注意:向查询字符串添加另一个参数的艾瑞克的建议就是这种方法的一个例子--发明了一个新的URI来覆盖新的用例。
另一方面,如果新的要求是扩展现有表示,以便在可用时包含新信息,则不需要创建新的URI,但应该使用附加数据扩展现有表示。
这又取决于是否有支持扩展的媒体类型。简单的部分是输入数据;更难的部分是确保现有客户端不会中断。
您需要的基本契约是表示中的可选字段的概念,以及消费者必须忽略他们不承认的字段的概念。原子联合规范是这个思想的一个很好的参考实现。
在普通的旧JSON中,这可能看起来像
{
"id": 1,
"serialNumber": "12345",
"connectedTo" : "78901"
}
或者,您可能更倾向于在表示中明确扩展的概念。
{
"id": 1,
"serialNumber": "12345",
extensions : {
"connectedTo" : "78901"
}
}
有很多不同的方法,你可以拼写这一点,但它真正的意义是确保您的表示是向后兼容的。
如果您的解决方案使用超媒体,则可以为扩展提供链接。
{
"id": 1,
"serialNumber": "12345",
connectedTo : {
"serialNumber" : "78901",
"href" : "/devices?serialNumber=78901"
}
}
链接很适合扩展点,因此您可能会将其反转。
{
"id": 1,
"serialNumber": "12345",
links : [
{
rel : "connectedTo",
href : "/devices/78901"
serialNumber : "78901"
}
]
}
https://softwareengineering.stackexchange.com/questions/340737
复制相似问题