sinatra自定义500 error和not_found
来自1.4.7
404
not_found所做的就是在@errors建立两个键值关系 :404 => “404处理块” ,Sinatra::NotFound => “404处理块”。可以看成是error(404){}的别名写法
def error(*codes, &block)
args = compile! "ERROR", //, block
codes = codes.map { |c| Array(c) }.flatten
codes << Exception if codes.empty?
codes.each { |c| (@errors[c] ||= []) << args }
end
# Sugar for `error(404) { ... }`
def not_found(&block)
error(404, &block)
error(Sinatra::NotFound, &block)
end
class NotFound < NameError #:nodoc:
def http_status; 404 end
end
当所有route都走完(没有halt回invoke时),会调route_missing,引发Sinatra::NotFound
def route!(base = settings, pass_block = nil)
if routes = base.routes[@request.request_method]
# routes.each ...
end
# Run routes defined in superclass ...
route_missing
end
def route_missing
if @app
forward
else
raise NotFound
end
end
于是handle_exception!
def dispatch!
invoke do
static! if settings.static? && (request.get? || request.head?)
filter! :before
route!
end
rescue ::Exception => boom
invoke { handle_exception!(boom) }
ensure
begin
filter! :after unless env['sinatra.static_file']
rescue ::Exception => boom
invoke { handle_exception!(boom) } unless @env['sinatra.error']
end
end
handle_exception!会发现Sinatra::NotFound的http_status,于是,设置status code 404
def handle_exception!(boom)
@env['sinatra.error'] = boom
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
status(500) unless status.between? 400, 599
if server_error?
dump_errors! boom if settings.dump_errors?
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
end
if not_found?
headers['X-Cascade'] = 'pass' if settings.x_cascade?
body 'Not Found
'
end
res = error_block!(boom.class, boom) || error_block!(status, boom)
return res if res or not server_error?
raise boom if settings.raise_errors? or settings.show_exceptions?
error_block! Exception, boom
end
之后error_block!,覆盖if not_found?中的body '<h1>Not Found</h1>'。
注意reverse_each,即是,not_found可多次定义,后来的覆盖先定的,子类的覆盖父类的
def error_block!(key, *block_params)
base = settings
while base.respond_to?(:errors)
next base = base.superclass unless args_array = base.errors[key]
args_array.reverse_each do |args|
first = args == args_array.first
args += [block_params]
resp = process_route(*args)
return resp unless resp.nil? && !first
end
end
return false unless key.respond_to? :superclass and key.superclass < Exception
error_block!(key.superclass, *block_params)
end
500
若不带参数,只带block来调用error方法,则是走handle_exception!的status(500)
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
然后error_block!又会重新找出500 => “500处理块”
当然,error可以用来定义其它error status code的处理方法