Reputation: 11508
I would like to serve up a generated static website (containing documentation) from the Rails public/
directory. My directory structure looks like this:
`-- rails-root
| [...]
|-- public
| |-- assets
| | |-- favicon.png
| | |-- fonts
| | | `-- ...
| | |-- images
| | | `-- ...
| | |-- javascripts
| | | `-- ...
| | `-- stylesheets
| | `-- ...
| |-- documentation
| | |-- GLOSSARY.html
| | |-- index.html
| | `-- ...
I am copying my generated files to rails-root/public/documentation
and everything is fine as long as I request it as http://<site>/documentation/
or http://<site>/documentation/index.html
.
However, when I request http://<site>/documentation
rails tries to be helpful and serves up my index.html
page. All the links in the page are relative so serving it up from a different base (root) instead of its directory (documentation/
) makes it impossible for it to load any assets, looks broken and prevents navigation from all links.
I've tried setting up a route to do a redirect in case someone requests http://<site>/documentation
(without a trailing slash):
get '/documentation', :to => redirect('/documentation/index.html')
This does not work, I'm guessing because the public
files take precedence over routing? I did try a bogus path just to validate that my routing code is correct and it worked:
get '/xxx', :to => redirect('/documentation/index.html')
I would like to either prevent rails from being helpful (I'm fine with a 404 for http://<site>/documentation
as long as trailing / works; 404 is better than a broken page) or make the redirect. I am using Rails 4.2. Any ideas welcome!
Upvotes: 1
Views: 381
Reputation: 124
The ActionDispatch::Static
middleware is responsible for serving static content, and for this behavior of serving a directory even when accessed without trailing slash.
There doesn't seem to be a nice way to change this behavior without monkey patching or forking this middleware.
But, if you're ok with the overhead, a workaround is to observe that the request path/path_info is changed by ActionDispatch::Static
and use this information to write a middleware that intercepts it and redirects directories without trailing slash:
module Rack
class RedirectStaticWithoutTrailingSlash
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
original_path_info = request.path_info
response = @app.call(env)
updated_path_info = request.path_info
if serving_index_for_path_without_trailing_slash?(original_path_info, updated_path_info)
redirect_using_trailing_slash(original_path_info)
else
response
end
end
def serving_index_for_path_without_trailing_slash?(original_path_info, updated_path_info)
updated_path_info.end_with?('index.html') &&
original_path_info != updated_path_info &&
!original_path_info.end_with?('/')
end
def redirect_using_trailing_slash(path)
redirect("#{path}/")
end
def redirect(url)
[301, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']]
end
end
end
This middleware will need to be configured to act before ActionDispatch
in your rails configuration:
config.middleware.insert_before('ActionDispatch::Static', 'Rack::RedirectStaticWithoutTrailingSlash')
The disadvantage of this approach is that you still do all the work of reading the file and preparing the request, and since it's not using any kind of document behavior, it may break in future rails versions.
Upvotes: 1