在RESTful Rails中,删除一个不存在的资源是否应该返回404错误?

26

在一个全新的Rails应用程序中,使用脚手架生成RESTful模型后,生成的删除代码如下:

class BeersController < ApplicationController
  # DELETE /beers/1
  # DELETE /beers/1.xml
  def destroy
    @beer = Beer.find(params[:id])
    @beer.destroy

    respond_to do |format|
      format.html { redirect_to(beers_url) }
      format.xml  { head :ok }
    end
  end
end
如果用户尝试删除同一种啤酒两次(可能是快速双击或在两个不同的浏览器选项卡中执行操作),他们将收到RecordNotFound错误,导致404页面。这是一种相当不友好的体验;完成重定向回到 beers_url 似乎更好,并可能带有 flash 错误,因为用户对第二次删除失败实际上无能为力。

另一种方法是表现得好像已成功删除,类似于以下内容:

def destroy
  @beer = Beer.find_by_id(params[:id])
  destroyed = @beer.try(:destroy)        

  respond_to do |format|
    format.html { redirect_to(beers_url) }
    format.xml  { destroyed ? head(:ok) : head(:not_found) }
  end
end

我可以理解在API使用情况下希望出现 404 硬错误的愿望,但是对于 Web 应用程序来说很难为我提供充分理由。有人能够提供一个良好的理由吗,为什么我们应该以 RESTful 的名义向用户抛出可怕的错误?

(这个问题不特定于 Rails,但我不知道其他框架如何处理这种情况)。


15
你不应该首先破坏啤酒...(!) - fletcher
这个问题涉及到了Rails。关于HTTP的更一般的问答已经存在了。 - Palec
@fletcher,你最好将它们标记为“未激活”或“已过期”。 - havryliuk
5个回答

12

11
必须导致所针对的资源与原来相同的服务器状态。这并不意味着您需要返回相同的状态码。在之前请求失败的情况下,您似乎假设会有重复请求。那么对于根本不存在的资源呢?试图删除这些资源应该清楚地指示它们不存在,不是吗? - Oliver Drotbohm
1
Gil,你的更新意味着它可以返回404而不违反特定原则,即幂等性。这并不意味着它应该这样做。话虽如此,从实际角度来看,我认为这是有道理的。这意味着客户端有机会区分项目是被删除还是不存在,这是用户和/或客户端开发人员可能关心的事情。我仍然想知道是否有任何标准适用于这种情况,这是基本的,应该有的。 - mahemoff
问题在于Rails在服务器端渲染404页面。唯一的方法是使用AJAX,使“客户端”能够将404和204视为相同。否则,Rails服务器必须拥有一个自定义错误页面,它看起来像成功,但只在这种情况下才是如此,这是可怕的设计。感谢REST架构师考虑了客户端! - Larry Kyrala
@LarryKyrala,您所描述的问题似乎是特定于技术的,并不影响REST架构的一般性。服务器返回任何错误代码(比如404或204),客户端可以自由地以任何方式呈现它。REST不规定客户端应该如何呈现内容。 - Gili

6
我认为你不应该为了遵循某些标准而将错误抛给用户,特别是对于面向消费者的应用程序(而不是B2B)。但你也不应该让你的API仅因为这种情况而更改其状态代码。资源不再存在,所以404是正确的响应。
我认为这里有一条最小阻力路径。我还没有探索过Ruby,因此无法提供任何可用的实现;但我对使用html/css/js的Web应用程序有一定经验。
如果用户点击按钮存在一些合理的问题,为什么不设置该按钮,以便在提交请求时禁用它,并在条件恰当时重新启用它(请求已返回)?换句话说,通过使得进入你看到的情况变得不可能,避免if(这种非常特殊的情况)逻辑。我假设Ruby有一些专门处理请求并添加不同状态代码的功能处理程序的东西,或者至少是非200状态代码。

2
我喜欢尽可能地防止非幂等操作的重复提交。虽然这并不是绝对可靠的(比如用户在两个标签页中打开了应用程序,并尝试在两个标签页中删除同一项),但它肯定可以提高可用性。 - Brad
我认为你想要的是“较少”或者仅仅是“少一些”。 - Aaron J Lang
你可以使用AJAX或不使用AJAX来防抖请求(发送后禁用按钮)。在收到第一个204响应后使条目消失是一个很好的额外功能。 - Larry Kyrala

2

Rails脚手架代码只是建议。你想要使错误信息更加用户友好的直觉是正确的。


2
我认为你说的没错,但我的观点更强烈;应该不会有错误提示信息,而且动作应该完成,就好像已经成功销毁了一样。 - Brad

1

你完全可以返回一个格式良好的404页面作为响应。状态码可以保持不变,只是呈现方式比典型的404页面更加用户友好。你甚至可以在响应体中返回“重定向”的页面。这种方法可能会遇到浏览器支持的问题。虽然已经过去了很多年,但我似乎记得IE(6?)在收到404时完全忽略响应体内容。你需要进行实验以找出最适合你的方法。


1

我在思考提供给客户的响应的安全性方面。如果你收到204,那么你知道资源确实存在,但是404则表示没有这样的资源。

不过,我不确定这可以被利用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接