REST API设计适用于来自不同域但具有共同关系的实体

时间:2016-07-13 08:26:59

标签: java api rest jersey jax-rs

在一般情况下,当一个实体与另一个实体有关系时,我会以这种方式嵌套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)和“魔术”逻辑,这将决定我是否想要使用新运算符或旧运算符创建行。

是否有处理此类情况的最佳做法?

1 个答案:

答案 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": [ ... ]
}