This post originated from an RSS feed registered with Ruby Buzz
by rwdaigle.
Original Post: What's New in Edge Rails: Simpler Conditional Get Support (ETags)
Feed Title: Ryan's Scraps
Feed URL: http://feeds.feedburner.com/RyansScraps
Feed Description: Ryan Daigle's various technically inclined rants along w/ the "What's new in Edge Rails" series.
Conditional-gets are a facility of the HTTP spec that provide a way for web servers to tell browsers that the response to a GET request hasn’t changed since the last request and can be safely pulled from the browser cache.
They work by using the HTTP_IF_NONE_MATCH and HTTP_IF_MODIFIED_SINCE headers to pass back and forth both a unique content identifier and the timestamp of when the content was last changed. If the browser makes a request where the content identifier (etag) or last modified since timestamp matches the server’s version then the server only needs to send back an empty response with a not modified status.
It is the server’s (i.e. our) responsibility to look for a last modified timestamp and the if-none-match header and determine whether or not to send back the full response. With this new conditional-get support in rails this is a pretty easy task:
1234567891011121314151617181920
classArticlesController < ApplicationControllerdefshow@article = Article.find(params[:id])# Set the response headers to accurately reflect the state of the# requested object(s) response.last_modified = @article.published_at.utc response.etag = @article# If the request's state is the same as the server's state then we know# we don't have to send back the whole bodyif request.fresh?(response) head :not_modifiedelse respond_to do |wants|# normal response processingendendend
The etag value is calculated for you with the etag= setter method. All you have to do is provide a single object or array of objects that uniquely identify this request. In this example the article itself contains all the information that uniquely identifies the state of this request. However, you may need to use more than one key in your app. For instance, if the request is user specific:
response.etag = [@article, current_user]
The request.fresh?(response) method is what will then tell you if the incoming request matches either the last-modified-since or if-none-match values of the outgoing response. If it does you can avoid passing the full body of the response back and save some bandwidth.
It’s also possible that you can avoid hitting the database all together if your application deals with completely static resources (though this is rare):
12345678910111213141516171819
classArticlesController < ApplicationControllerdefshow# If articles don't change, the etag can be based solely# on items we have in the request response.etag = [:article, params[:id]]# If the request's state is the same as the server's state then we can# avoid the db call all togetherif request.fresh?(response) head :not_modifiedelse@article = Article.find(params[:id]) respond_to do |wants| ...endendend
So be a good citizen and make your requests conditional-get compatible. It’s the right thing to do – and can make your apps more performant.