为什么它不起作用 - 使用源代码!
让我们看看 Sinatra 源代码,看看为什么这个问题不起作用。主要的 Sinatra 文件 (lib/sinatra/base.rb) 只有 2043 行长,而且代码可读性很强!
所有halt 所做的是:
def halt(*response)
response = response.first if response.length == 1
throw :halt, response
end
异常被捕获:
# Dispatch a request with error handling.
def dispatch!
invoke do
static! if settings.static? && (request.get? || request.head?)
filter! :before
route!
end
rescue ::Exception => boom
invoke { handle_exception!(boom) }
[..]
end
def handle_exception!(boom)
@env['sinatra.error'] = boom
[..]
end
但由于某种原因,这段代码永远不会运行(用基本的“printf-debugging”测试过)。这是因为在invoke 中,该块的运行方式如下:
# Run the block with 'throw :halt' support and apply result to the response.
def invoke
res = catch(:halt) { yield }
res = [res] if Fixnum === res or String === res
if Array === res and Fixnum === res.first
res = res.dup
status(res.shift)
body(res.pop)
headers(*res)
elsif res.respond_to? :each
body res
end
nil # avoid double setting the same response tuple twice
end
请注意此处的catch(:halt)。 if Array === res and Fixnum === res.first 部分是 halt 设置的内容以及响应正文和状态码的设置方式。
error 403 { .. } 块在 call! 中运行:
invoke { error_block!(response.status) } unless @env['sinatra.error']
所以现在我们明白了为什么这不起作用,我们可以寻找解决方案;-)
那么我可以用某种方式停止吗?
据我所见。如果您查看invoke 方法的主体,您会发现在使用halt 时始终设置主体。你不想要这个,因为你想覆盖响应正文。
解决方案
使用“真正的”异常,而不是halt“伪异常”。 Sinatra 似乎没有预定义的异常,但 handle_exception! 确实会查看 http_status 来设置正确的 HTTP 状态:
if boom.respond_to? :http_status
status(boom.http_status)
elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
status(boom.code)
else
status(500)
end
所以你可以使用这样的东西:
require 'sinatra'
class PermissionDenied < StandardError
def http_status; 403 end
end
get '/error' do
#halt 403, 'My special message to you!'
raise PermissionDenied, 'My special message to you!'
end
error 403 do
'Error message -> ' + @env['sinatra.error'].message
end
按预期工作(输出为Error message -> My special message to you!)。您可以在此处返回 ERB 模板。