来自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的处理方法