How does rails server static file in public directory

原创文章,转载请注明来源并保留原文链接

Rails3是如何处理在public目录下的静态文件的?

首先要说的是,Rails3.1在生产环境下默认是不开启静态文件服务的,因为Apache或者Nginx这些web服务器可以帮我们做这些,所以如果在生产环境下出现404,您可能需要在config/environments/production.rb中开启:

# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = true

我们知道在Rails3之后,Rails和Rack的关系很大,提到Rack就不得不说中间件了(middleware),先来看看Rails中用到了哪些middleware:

rake middleware

use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
run Blog::Application.routes

OK,我们可以看到ActionDispatch::Static就是这个,是用来处理静态文件的。
看过ActionDispatch::Static的源码不难发现,用的主要是FileHandler:

class Static
  def initialize(app, path, cache_control=nil)
    @app = app
    @file_handler = FileHandler.new(path, cache_control)
  end
end

FileHandler符合标准的Rack中间件的定义,相应call方法:

class FileHandler
 def initialize(root, cache_control)
   @root          = root.chomp('/')
   @compiled_root = /^#{Regexp.escape(root)}/
   @file_server   = ::Rack::File.new(@root, cache_control)
 end

 def call(env)
   @file_server.call(env)
 end
end

原来就是`rack/utils`中的Rack::File了。Rack::File通过Request的路径到相应的目录下寻找文件。

通过ActionDispatch::Static的测试用例也可以看出它的作用:

def test_serves_static_index_at_root
  assert_html "/index.html", get("/index.html")
  assert_html "/index.html", get("/index")
  assert_html "/index.html", get("/")
  assert_html "/index.html", get("")
end

def test_serves_static_file_in_directory
  assert_html "/foo/bar.html", get("/foo/bar.html")
  assert_html "/foo/bar.html", get("/foo/bar/")
  assert_html "/foo/bar.html", get("/foo/bar")
end

def test_serves_static_index_file_in_directory
  assert_html "/foo/index.html", get("/foo/index.html")
  assert_html "/foo/index.html", get("/foo/")
  assert_html "/foo/index.html", get("/foo")
end

OK,到这里就应该清楚了,下面就只要指定路径就行了,也就是”Public”,在Rails::Application文件中:

if config.serve_static_assets
  middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
end

Leave a Reply

Your email address will not be published. Required fields are marked *