2013-12-10 55 views
2

让我们假设我有一个对象,该对象在我的应用程序中作为REST资源公开。该对象具有许多字段并包含许多其他对象,包括关联的集合。像这样的事情,但想到要大得多:REST资源的动态表示

Customer 
    List<Order> orders 
    List<Address> shippingAddresses; 
    // other fields for name, etc. 

Order 
    List<Product> products 
    // fields for total, tax, shipping, etc. 

Product 
    // fields for name, UPC, description, etc. 

我在API暴露客户为/customer/{id}

我的一些客户将要为每个订单的每一件产品的所有细节。如果我遵循HATEOAS,我可以提供链接以获取产品详细信息。这将导致n + 1呼叫该服务,以在客户的订单内填充产品。另一方面,如果我总是填充它,那么许多客户端会收到一些他们不需要的信息,我会做大量不需要的数据库查询。

如何根据客户的需求允许我的资源的客户表示?

我看到几个选项。

  1. 使用Jackson的JsonView注释事先指定使用的内容。来电者要求适合他们的观点。即/customer/{id}?view=withProducts。这需要我在编译时指定所有可用的视图,并不会那么灵活。

  2. 允许呼叫者请求在请求中填充某些字段,即/customer/{id}?fields=orders,firstName,lastName。这将需要我有一些处理程序可以解析f​​ields参数,并可能使用反射来填充东西。听起来对我来说超级混乱。你对子资源做了什么。我能做fields=orders.products.upc并以这种方式加入集合吗?听起来就像我试图在REST或其他东西上编写hibernate一样。

  3. 按照HATEOAS,并要求客户端进行百万次HTTP调用,以填充他们需要的东西。对于那些不想在大多数情况下填充物品的人来说,这种方法非常有效,但对于试图展示订单细节总结或其他类似内容的人而言,这会很昂贵。

  4. 具有独立的资源,为每个视图...

  5. 其他?

回答

1

我会做这样的事情: /客户/ {ID} /命令/包括=实体

这是一种你选择1的更具体的变化。

你还能有以下几种选择:

  1. 特定的顺序从特定客户没有产品清单: /客户/ {ID} /命令/(编号)
  2. 只是订单没有产品的客户: /客户/ {ID} /命令/

我倾向于避免单一资源,becau大部分时间,或者最终有人总是想要一个事物清单。

+0

根据我的经验,没有以下id的'/ customer'会给出这个列表,'/ customer/{id}'是你将它缩小到单个客户的方式,但这只是一种风格。这是'包含=实体'的东西,这是有趣的部分。这意味着我基本上只有2个选项,一个充分充实,另一个只包含字段。我认为这几乎不够灵活,特别是如果某些相关集合需要昂贵的操作来填充。 – digitaljoel

+0

那么不是说你是否想包含某个实体,你可以通过说出你想包含哪个实体来解决它。/customers/{id}/orders /?products = yes&productdetails = yes和/ customers/{id}/orders/{id}只会给你一个带该id的准系统订单。 –

+0

是啊,那基本上是我的#2。听起来像没有这个魔术弹头。 – digitaljoel

1

选项2(客户端指定字段)是一种过滤方法,与GETable资源相比,它更像查询接口。如果您接受POST请求中的部分模板,您的过滤器可能会更具表现力,您的服务将会填充。但这很复杂。

我愿意赌你所需要的是任何复杂实体的2个简单表示。这应该可以处理您域中99.9%的案例。鉴于此,请制作更多的URI,每个URI都用于每个事物的“视图”。

要处理0.1%的情况(例如,当您需要完全填充Products集合时),请为允许过滤的嵌套实体提供查询接口。您甚至可以提供超媒体链接来检索这些集合,作为上述简化表示的一部分。

+0

因此,基本上每个视图都有不同的处理程序。我可以使用JsonView为我进行序列化,并且可以获取关联的集合,而另一个则不会。 – digitaljoel

+0

这是一个很好的方法来做到这一点。认识到这个实体并不是真正的PUTable,因为它只是一个方便的表示层信息汇编。您在这里有很大的灵活性,因为客户实际上是一个单独的资源,不知道订单等。您可能会考虑对此资源使用不同的名称。 –