从救援块中引发另一个错误会发出将我们带入救援块的原始错误

Rak*_*kib 5 ruby runtime-error ruby-on-rails exception ruby-on-rails-4

案子

\n\n

我有一个 Rail 4 应用程序控制器,index其中包含如下方法:

\n\n
def index\n  raise "Original Error"\nrescue => e\n  puts "We caught: \'#{e.inspect}\'"\n  raise "Another Error"\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

我正在捕捉Original Error,处理它,按摩它,做任何我想做的事情,然后抛出一个完全不同的错误 - Another Error

\n\n
\n\n

期望

\n\n

因此,预计Another Error当我访问控制器的索引页面而不是Original Error.

\n\n
\n\n

意想不到的行为

\n\n

但我实际上得到的是Original Error相反的。将控制器#action 的日志复制到此处:

\n\n
Started GET "/homepage" for 127.0.0.1 at 2019-02-06 18:50:19 +0800\nWe caught: \'#<RuntimeError: Original Error>\'\nCompleted 500 Internal Server Error in 205ms (Flexirest: 0.0ms for 0 calls | ActiveRecord: 9.7ms)\n\nRuntimeError - Original Error:\n  app/controllers/homepage_controller.rb:6:in `index\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

为什么它抛出的是Original Error这里而不是Another Error

\n\n
\n\n

但是,控制台中的行为仍然符合预期

\n\n

我将相同的方法行复制粘贴index到 Rails 控制台中,然后index直接从控制台内调用该方法。这是控制台输出。

\n\n
[49] pry(main)> def index\n[49] pry(main)*   raise "Original Error"\n[49] pry(main)* rescue => e\n[49] pry(main)*   puts "We caught: \'#{e.inspect}\'"\n[49] pry(main)*   raise "Another Error"\n[49] pry(main)* end\n=> :index\n\n[50] pry(main)> index\nWe caught: \'#<RuntimeError: Original Error>\'\nRuntimeError: Another Error\nfrom (pry):67:in `rescue in index\'\n[51] pry(main)>\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf

\n

Bor*_* B. 2

从 Ruby 2.1 开始,该方法有一个使用“环境异常”的Kernel#raise默认参数:cause$!

  raise(string, cause: $!)
Run Code Online (Sandbox Code Playgroud)

其效果是,在环境异常范围内(即在块中rescue)引发的每个异常都将使用cause当前异常填充引发的异常的属性,除非调用者为causewhile指定不同的值raise

您的网络服务器/中间件可能正在挖掘链条cause以查找“根本原因”错误并显示该错误,可能使用类似e = e.cause until e.cause.nil?.

如果你想抑制这种行为,你可以指定nilcausewhile raise

raise "Another Error", cause: nil
Run Code Online (Sandbox Code Playgroud)