问题 是否应该在RESTful Rails中删除404中不存在的资源?


在带有支架RESTful模型的全新Rails应用程序中,生成的删除代码如下所示:

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

如果用户尝试两次删除相同的Beer(可能是快速双击,或者在两个不同的浏览器选项卡中执行操作),则会得到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应用程序辩护。任何人都可以提供一个很好的理由说明为什么我们应该以RESTfulness的名义向用户抛出一个可怕的错误?

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


12523
2018-06-24 22:02


起源

你不应该首先摧毁啤酒......(!) - fletcher


答案:


HTTP DELETE 是一个 幂等 操作。连续多次调用它必须导致与第一次相同的行为。含义: 你不应该返回HTTP 404

UPDATE:事实证明我错了: https://stackoverflow.com/a/24713946/14731


6
2018-05-19 06:31



必须导致目标资源的服务器状态相同。这并不意味着您需要返回相同的状态代码。如果先前失败,您似乎会重复请求。那些普通的不存在的资源呢?试图删除那些应该清楚地表明不存在,不应该吗? - Oliver Gierke
@OliverGierke,我已经更新了答案。 - Gili
吉尔,你的更新意味着它 能够 返回404而不违反特定原则,即幂等性。它并不意味着它 应该。也就是说,我认为从实际角度来看是有道理的。这意味着客户端有机会区分项目是否被删除以及项目是否不存在,这是用户和/或客户开发人员可能会关心的。我仍然想知道这种情况是否有任何标准,它应该是基本的。 - mahemoff
这里的问题是Rails是服务器端呈现404页面。 “客户端”可以同时处理404和204的唯一方法是AJAX。否则,Rails服务器必须有404的自定义错误,否则看起来会成功,但只有在这种情况下,这是一个可怕的设计。感谢REST架构师考虑客户端! - Larry Kyrala
@LarryKyrala您所描述的问题似乎是特定于技术的,并且一般不会影响REST架构。服务器返回任何错误代码(例如404或204),客户端可以自由地呈现它,但它希望如此。 REST不强制客户端如何呈现内容。 - Gili


我认为你不应该为了保持一些标准而向用户抛出错误 - 特别是如果这是一个面向消费者的应用程序(而不是B2B)。但是你也不应该只为这种情况改变你的api状态代码。资源不再存在;所以404是一个恰当的回应。

我认为这里有一条最小的路径(或者更少的 - 甚至是一个词)。我还没有探索ruby,所以我无法提供任何可用的实现;但我对使用html / css / js的网络应用程序有一定的经验。

如果用户点击两次按钮存在一些合法问题;为什么不设置按钮以便在提交请求时禁用它,并在条件正确后重新启用(请求已经返回)?换句话说,通过使得无法进入你所看到的情况来避免if(这种非常具体的情况)逻辑。我假设ruby专门用于处理请求和为不同的状态代码添加函数处理程序;或至少非200状态代码。


4
2018-06-24 22:14



我喜欢尽可能防止双重提交非幂等行为的想法。这不是万无一失的(想象一下,用户在两个标签中打开了两个应用程序副本,并尝试删除两个标签中的相同项目),但这绝对是可用性方面的改进。 - Brad
我想你想要“较小”或只是“少” - Aaron J Lang
您可以使用或不使用AJAX去除请求(在发送后禁用按钮)。收到第一个204之后使条目消失是一个很好的额外。 - Larry Kyrala


Rails脚手架代码充其量只是一个建议。您使错误消息更加用户友好的直觉是一个很好的直觉。


2
2018-06-24 22:13



我认为你是对的,但我正在做出更强烈的主张;根本不应该有错误消息,并且该操作应该完成 仿佛 毁灭成功了。 - Brad


没有理由你不能用你的回复返回一个格式良好的404页面。状态代码可以是相同的,只是渲染比典型的404页面更加用户友好。您甚至可以在响应正文中返回“重定向”页面。一个可能的问题是浏览器支持。这已经有好几年了,但我似乎回想起IE(6?)在收到404时完全无视响应主体内容。你必须尝试看看哪种方式最适合你。


1
2018-06-25 03:34