来自1.4.7

在调用route!之前,会先检查是否有对应的静态文件的

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


settings返回的是Base自身

def self.settings
  self
end

def settings
  self.class.settings
end


而Base自身设置的static?其实是检查是否有存放静态文件的目录(默认当前脚本所在目录的子目录public)

set :public_folder, Proc.new { root && File.join(root, 'public') }
set :static, Proc.new { public_folder && File.exist?(public_folder) }


然后在目录里检查路径存不存在,存在则send_file

def static!(options = {})
  return if (public_dir = settings.public_folder).nil?
  path = File.expand_path("#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" )
  return unless File.file?(path)

  env['sinatra.static_file'] = path
  cache_control(*settings.static_cache_control) if settings.static_cache_control?
  send_file path, options.merge(:disposition => nil)
end


send_file使用Rack::File#serving来做一些缓存检查和长度设置,并返回rack标准的[status, header, body]。成功的话,最后会halt,不走route!了

def send_file(path, opts = {})
  if opts[:type] or not response['Content-Type']
    content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
  end

  disposition = opts[:disposition]
  filename    = opts[:filename]
  disposition = 'attachment' if disposition.nil? and filename
  filename    = path         if filename.nil?
  attachment(filename, disposition) if disposition

  last_modified opts[:last_modified] if opts[:last_modified]

  file      = Rack::File.new nil
  file.path = path
  result    = file.serving env
  result[1].each { |k,v| headers[k] ||= v }
  headers['Content-Length'] = result[1]['Content-Length']
  opts[:status] &&= Integer(opts[:status])
  halt opts[:status] || result[0], result[2]
rescue Errno::ENOENT
  not_found
end