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