Problem with Rails page caching and automatic extensions
I have a JSON
and XML
based API that needs to be page cached. I have setup my routes on the api to include the format as part of the URL, such that URL's like this work:
http://example.com/foo/1/bar/2/xml
http://example.com/foo/1/bar/2/json
The problem I am seeing is that in the server's public
folder, the files are being saved as xml.xml
and json.json
, and this causes cache misses the next time the URL is accessed.
Is there a way to either:
- Turn off the auto extension generation so that they are saved without an extension at all? (EX:
RAILS_ROOT/public/foo/1/bar/2/json
) - Force all the extensions to be
.html
for every call. (EX:RAILS_ROOT/public/foo/1/bar/2/json.html
)
Either of these would cause my server to return the cached file instead of a miss. How can I do this?
EDIT:
Somebody asked for the relevant route:scope '(foo/:foo_id)', :foo_id => /\d+/ do
get '/bar/:bar_id/:format' => 'bars#show', :bar_id => /\d+/, :format => /json|xml|html/
end
SOLUTION:
While I was looking for an official way to make this happen开发者_高级运维 using the built in page caching support, I ended up just using an after filter and my own page cache method, as suggested by Anton
# application_controller.rb
def cache_api_page
if REDACTEDServer::Application.config.action_controller.perform_caching
self.class.cache_page(response.body, request.path, '')
puts "CACHED PATH: #{request.path}"
end
end
# bar_controller.rb
after_filter :cache_api_page, :only => [ :show, :index ]
You can do that like this:
class FooController < ApplicationController
after_filter(:only => :show, :if => Proc.new { |c| c.request.format.json? }) do |controller|
controller.class.cache_page(controller.response.body, controller.request.path, '.html')
end
end
When http://example.com/foo/1/bar/2/json is accessed, it will write page to cache (RAILS_ROOT/public/foo/1/bar/2/json.html)
And if you get http://example.com/foo/1/bar/2/json again, you receive RAILS_ROOT/public/foo/1/bar/2/json.html, but your http server(Apache?) should know about content type of this files.
Otherwise content type will set to 'text/html'
UPDATE
To you .htaccess
<FilesMatch "\/json$">
<IfModule mod_headers.c>
Header set Content-Type "text/json"
</IfModule>
</FilesMatch>
<FilesMatch "\/xml$">
<IfModule mod_headers.c>
Header set Content-Type "text/xml"
</IfModule>
</FilesMatch>
In your application.rb configuration block try adding:
config.action_controller.page_cache_extension = '.html'
It should ignore the extension calculated from the request, and always use this. You could also try using this with an empty string instead.
EDIT: actually this won't work because this only sets a default. If the request has an extension (in your case, concluded from :format) it will be used.
I suggest changing :format in your routes to something else, that rails will not give special meaning to, like :fmt. Then rails should not add the extension and default to '.html'.
EDIT2: If you have to use :format you can monkeypatch Rails:
ActionController::Caching::Pages::ClassMethods
private
def page_cache_file(path)
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
name << page_cache_extension #unless (name.split('/').last || name).include? '.'
return name
end
end
Notice the comment sign I've added before 'unless'. This is the part that overrides the default from my first answer.
精彩评论