我在Django中有两个模型:
class Thread(models.Model):
entity = models.ForeignKey(Entity, null=True, blank=True)
...
class ThreadMessage(models.Model):
thread = models.ForeignKey(Thread, related_name='messages')
author = models.ForeignKey(User)
...
现在客户想要创建一个包含第一条消息的新线程。它首先要POST /threads
创建新主题并查找其id
,然后POST /messages
在thread
字段中传递找到的ID。
我在想,如果在Ember的一个请求中完成所有这些操作是合理的,也可以这样做:
POST /messages
{"message": {"text": "text", ...},
"thread": {"entity": 1}}
响应将是:
{"message": {"text": "text", "id": 5678, "thread": 1234, ...},
"thread": {"entitity": 1, "id": 1234, ...}}
答案 0 :(得分:5)
是的,这是完全合理的。
人们似乎以一种非常奇怪且非常无知的方式解释REST。粗略阅读HTTP RFC 7231的POST和PUT将证实你已经有了坚实的基础。
资源表示可以表示ANYTHING。关键是要保留REST操作的语义。所以PUT可以用于CREATE和REPLACE之类的操作(我倾向于认为PUT是REPLACE而不是UPDATE,因为REPLACE更接近于幂等语义而不是UPDATE)
支持端点的PUT应该接受GET返回的任何表示。 POST可以做任何你想要的东西,因为它不需要支持幂等语义。
HTTP和REST旨在支持可能与其他资源重叠的资源表示,并且RFC明确说明了这一点。在集合端点上执行GET时,您始终执行此操作。
您不是通过在单个请求中使用包含子消息的线程来破坏REST,并且IMO是服务器上的一个非常有效的用例,用于理智的参照完整性。每当需要事务语义时,POST或PUT完全有效,可以在单个请求中创建服务器上的对象图。这很简单,如果你可以在一个请求中获取它,你应该能够在一个请求中将它PUT,所以要仔细考虑你的URL和参数。
例如,您可能有一个返回所有消息的线程端点,并且该端点可能支持一个参数,只返回仅返回/api/threads?include=hasRead
和id
的信息hasRead
的某个子集。对于线程中的每条消息,或者可能只是某些范围的“页面”。然后,您可以使用相同的端点和参数进行PUT,只需批量更新hasRead
属性。
任何对此感到困惑的人都可能从未考虑过访问控制。访问控制需要基于允许访问的内容从一个用户到另一个用户的不同资源视图。资源的这种不同视图在HTTP认证头和/或请求URL中传达;再次,REST不会被子设置或重叠资源打破。
所以继续创建你需要的对象的最小图形,然后PUT或POST它们。我使用V4 UUID,因此客户端可以自己分配ID(以及资源端点),这使我可以使用PUT创建和替换类似的操作,并在没有客户端< - >服务器ID映射问题的情况下连接复杂的对象图。
答案 1 :(得分:-4)
您要做的就是破解REST和EmberJS本身的概念。
如果您有两个单独的API you should make two REST calls.
首先保存父thread
模型,成功返回后保存子message
。然后使用addObject来反映视图中的更改。
这是最好的方法。不要试图通过减少API调用来优化并打破REST。