在单个REST调用中执行多个数据库操作

在单个REST调用中执行多个数据库操作

问题描述:

我正试图找出调用REST操作的最佳方法,这些操作可以从一次调用执行多个操作和多个数据库更新。在单个REST调用中执行多个数据库操作

在我的数据模型中,我有Diners和LunchBoxes和Foods。午餐盒只是食客和食物之间的多对多关系,但具有计数属性,表明给定的晚餐有多少种类型的食物。

我想要建立一个表明晚餐已经吃掉他们的食物之一的调用,这相应地增加了餐厅的健康。某些食物比其他食物更有营养,因此会增加不同量的餐饮者的健康。构成这一行动将是:

  • 减少对晚餐的饭盒计数属性为给定的食品通过正确的量
  • 增加晚餐的健康因此

所以两个表必须这里更新:晚餐和午餐盒,都在一次交易中。

试图用名词,最好我能想出是:

POST /餐厅/坦白/餐

其中描述了一顿XML将类似于

<meal> 
    <food> 
    <id>apple</id> 
    </food> 
    <count>2</count> 
</meal> 

然而,这让我觉得很有意思。在REST中发布膳食应该创建膳食资源。在这种情况下,我们不仅创建一个Meal资源,而且还更新其他两个资源:Diner和LunchBox。

我想一种方法是让客户端在两个不同的调用中处理这个问题 - 一个更新Diner,一个更新LunchBox。但是,这似乎是错误的,因为我们有多个客户端(HTML,Flash等),都需要执行此操作。如果我们将来更新用于消费食物的业务逻辑,那么我们需要在许多客户端上进行更改,而不是在单个服务器上进行更改。

他人如何接近这个公认的非常基本的问题?

如果你看一下作为晚餐,而不是资源本身的作用,这使得更多的意义。不过,我会试图将名称改为动词,如。在为REST系统建模时,您做出的一些决策将是任意的。从理论的角度来看,这个动作可以在用餐者LunchBox上。我倾向于按我的应用程序是如何使用模型,所以有什么用UI适合,什么是更容易在文档中解释给第三方等

没有什么在REST模式,决定了底层结构或排除了你在操作中处理相当复杂的事务。在这种情况下,我只需要一个操作,根据需要处理所有使用事务的逻辑。

该行动将对一名餐馆进行操作,并列出食物和数量。

在Rails你现在将不得不像

# routes.rb 
map.resources :diner, :member => {:eat => :post} 

#controller 
def eat 
    @diner = Diner.find(params[:id]) 
    @diner.eat(params[:foods]) 
    respond_to ... 
    end 
end 

你会发现,其实我已经推了逻辑到模型中。我假设Diner模型与LunchBox模型有关联。方法方法将增加健康和改变相关午餐盒中的食物量。这样你可以非常整齐地封装所有的逻辑。

UPDATE 我认为这是一个相当普遍的模式,使资源具有一些​​特定的命名操作。我经常只是将操作添加到我的控制器中,但通过使用HTTP和Rails约定公开这些操作来保持REST的一般框架。

您当然可以将您的系统与Meal建模为一个资源,但我认为这会导致您的需求更加复杂。

也可以使用只对进行建模,但是对于真实世界的系统来说,它很笨重笨拙。在这个世界观中,您开始走下去协调多个http动作的路径来组成更高阶的API。这样的系统几乎不可能用一半体面的用户界面来构建,如果你将API暴露给第三方,他们会讨厌你。

+0

谢谢。不过,我在这里看到了一些我想问的问题。首先,不是“吃”一个令人困惑的选择,因为它是一个动词?这就是为什么我想用“餐”的原因,因为然后你发布一个新的餐(即关于REST资源的全部名词是名词)。其次,由于行动不是幂等的,不应该是POST而不是PUT?最后,这意味着即使用户不一定要求更新该模型,那么/餐厅/坦率/吃饭也会有更新LunchBox模型的副作用。在REST下还是犹太教吗? – mrjake2 2010-01-30 01:11:18

+0

开始发表评论,但是当我用完字符时修改了我的帖子 – 2010-01-30 02:19:50

+0

哦,你说得对vs post,代码已经修改,我从routes.rb剪切/粘贴我有方便。 – 2010-01-30 02:27:22

首先,餐厅和饭盒的更新应该在一个请求中完成。不要陷入尝试通过REST API执行事务的陷阱。

在我们谈到您的具体问题之前,让我们为客户如何与您的服务进行交互以引导您的问题奠定基础。

客户端应该始终从根服务url开始。

GET /DiningService 
Content-Type: application/vnd.sample.diningservice+xml 
200 OK 

<DiningService> 
<Link rel="diners" href="./diners"/> 
<Link rel="lunchboxes" href="./lunchboxes"/> 
<Link rel="foods" href="./foods"/> 
</DiningService> 

我不知道你的用户将与客户端软件的交互方式,还是让我们假设,我们首先需要确定谁是打算做饮食。我们可以通过查看与rel =“diners”链接的响应来检索用户列表,并按照该链接进行查看。

GET /DiningService/diners 
Content-Type: application/vnd.sample.diners+xml 
200 OK 

<Diners> 
<Diner Name="Frank"> 
    <Link rel="lunchbox" href="./Frank/lunchbox"/> 
</Diner> 
<Diner Name="Bob"> 
    <Link rel="lunchbox" href="./Bob/lunchbox"/> 
</Diner> 
</Diners> 

返回的是用餐者列表。为了简单起见,我选择了创建自定义媒体类型,但是对于这些列表,您最好使用Atom提要等。 客户需要识别弗兰克作为餐厅,所以现在我们想要访问他的饭盒。我们的自定义媒体类型的规则说,弗兰克的午餐盒 的链接可以在带有rel =“饭盒”的链接元素中找到。 我们从响应文档中获取该URL并按照它进行操作。

GET /DiningService/Frank/lunchbox 
Content-Type: application/vnd.sample.lunchbox+xml 
200 OK 

<Lunchbox> 
<Link rel="diner" href="/DiningService/Frank"/> 
<Food Name="CheeseSandwich" NutritionPoints="10"> 
      <Link rel="eat" Method="POST" href="/DiningService/Frank?food=/DiningService/Food/CheeseSandwich"/> 
</Food> 
<Food Name="CucumberSandwich" NutritionPoints="15"> 
    <Link rel="eat" Method="POST" href="/DiningService/Frank?food=/DiningService/Food/CucumberSandwich"/> 
</Food> 
</Lunchbox> 

我们得到的回复是另一种自定义介质类型定义描述我们可以用饭盒做一个饭盒和链接的内容。一旦客户选择吃饭的食物,我们可以通过寻找与rel =“eat”的链接并遵循该URL来识别要跟随的URL。在这种情况下,这是一个帖子。

POST /DiningService/Frank?food=/DiningService/Food/CucumberSandwich 
Content-Type: None 
200 OK 

我没想到太难什么构建url的最好的办法就是,因为如果我改变了主意,下周并使其

<Link rel="eat" Method="POST" href="/DiningService/Frank/Mouth?food=/DiningService/Food?id=759"/> 

甚至

​​

这对客户来说并不重要,因为它会继续寻找rel =“eat”的链接,并且会跟随该URL。您可以选择任何适用于您选择的Web框架的最简单的URL结构。 URL结构属于服务器,您应该可以随时对其进行更改,并且对客户端几乎没有影响。

如果你采取这种方法,你可以停止强调提出完美的网址。这种“RESTful URL”的虚假概念已经做得更多,以防止人们学习REST而不是SOAP。

+0

谢谢,这个例子帮助我多思考了这个过程。尽管如此,我还是不太了解其中的一点 - 如果REST应用程序中的每个项目都应该是一个资源,那么我不会了解上次调用中所暴露的资源。在电话/ DiningService/Frank?food =/DiningService/Food/CucumberSandwich中,资源是什么?是弗兰克吗,还是CucumberSandwich? – mrjake2 2010-01-30 04:38:49

+0

在那个特定的URL中,资源是Frank。您可能会争辩说,将食物网址发布到用餐者资源的含义并不十分清楚。在第二个URL中,我创建了一个名为“mouth”的特殊“处理资源”,它是餐馆的一个子资源,当您向口腔资源发布食物URL时,会执行吃饭操作。当然,它有点愚蠢,但我的观点是,只要你正确地使用动词,那么url的内容就不重要了。 POST行为没有真正定义,所以很难误用。 – 2010-01-30 05:30:51