在一般情况下,当一个实体与另一个实体有关系时,我会以这种方式嵌套REST资源:
POST /user/{userId}/accounts
来自同一域的实体也可以。但是当谈到来自不同领域的实体时,它没有意义。例如,我有总线(@Entity Line
)和运算符(@Entity Operator
)。每一行都有一个操作员:
@ManyToOne
@JoinColumn(name = "operator_id")
private Operator operator;
所以如果我需要创建新的总线,我必须通过运营商。
没有问题,如果我需要使用new运算符创建行,但如果我只想引用运算符,我需要以某种方式传递operator_id。一些想法如何处理:
1。运营商中的嵌套线
POST /operators/{operatorId}/lines {name: "15B", type: "BUS"}
从技术角度来看,这是可以的,但我希望将运算符和行分开,因为行并不真正“归属”(嵌套)给运算符。
2。直接传递operatorId
POST /lines {name: "15B", type: "BUS", operator: 12}
这件事有些问题。有一种情况,当我想用新操作符创建新行时,查询将如下所示:
POST /lines {name: "15B", type: "BUS", operator: {name: "SuperBUS"}}
我需要处理这两种情况。这将带来额外的实体(因为原始的具有Operator operator
,而不是int operator
)和“魔术”逻辑,这将决定我是否想要使用新运算符或旧运算符创建行。
是否有处理此类情况的最佳做法?
答案 0 :(得分:2)
以下是一些想法,可能会或可能不会帮助您决定使用什么:
传递ID
传递 Id 将客户端与对象的某些实现细节相结合。这通常是需要避免的。请考虑以下代码:
public Line createLine(String name, LineType lineType, int operatorId);
调用者必须知道 operatorId ,它通常不是模型的一部分。 id 通常只是持久性的实现细节,换句话说,运算符实际上没有 id ,它有一个< em> name 最多,但实际上可以改变。
在&#34;正常&#34; OO代码,这个方法可能看起来像这样:
public Line createLine(String name, LineType lineType, Operator operator);
在参考的帮助下,改为传递实际的运算符。 RESTful HTTP中对象引用的等价物是什么?当然是URI
。这样我们就可以了:
POST /lines
{"name": "15B", "type": "BUS", "operator": "http://something/124"}
此方法存在另一个问题,存在于Java和HTTP API中:我们无法确定我们得到的对象类型。在Java中,如果 Operator 是一个接口,我们不知道我们得到了什么实现。例如,该实现可能不在我们的数据库中。 HTTP API也是如此。如果我们接受 URI ,我们就无法确定该资源的来源,也许它甚至不支持我们的media-types
。
这可能不是你想要的。
提供背景
这就是你所说的&#34;嵌套&#34;。考虑Java代码,这相当于:
public interface Operator {
...
Line createLine(String name, LineType lineType);
...
}
在HTTP中:
POST /operator/123/lines
{"name": "15B", "type": "BUS"}
这可能更合适,即使运营商并非真正拥有&#34;线条。服务器可以检查重复的行或类似的语义规则。
<强>超媒体强>
如果你真的希望Lines是独立的实体,你必须以某种方式引用运算符。 Ids 会引入耦合,但提供 URI s过于通用,那么唯一的解决方案是引入表单。
正确实施可能会更复杂。关键是要使表单与HTML相似。只需使用超媒体提供适合客户的选项。
这样的事情:
GET /lines
{"create": {
"operator": {
"type": "select",
"values": [ ... ]
}
},
"lines": [ ... ]
}