Hack 59. Find Out Whether Ajax Is Calling in the Request
Discover whether XMLHttpRequest or a URL in a browser is requesting an action.
One of the issues with defining simple actions in Ruby on Rails is that once an action that responds with a fine-grained value (such as a string or small HTML chunk) is defined, users can make direct requests for that little value in their browsers. But chances are, those values were designed only for XMLHttpRequest objects.
For example, "Periodically Make a Remote Call" [Hack #61] uses the request object to periodically get a string representing the current date. It calls an action named increment, which looks like this in the controller object:
def increment tz=TimeZone.create("TZ",-60*60*5) render :text => tz.now( ).to_s end end
You don't want the users to be able to call this action directly in their browsers, as in http://localhost:3000/hacks/increment, because it is designed only for calling by Ajax behind the scenes.
Are You XMLHttpRequest?
Thankfully, the Rails API has a method that can detect whether a request involves XMLHttpRequest or not. Here's how we can change our action in the controller:
def increment tz=TimeZone.create("TZ",-60*60*5) if @request.xml_http_request?( ) render :text => tz.now( ).to_s end end
This action renders the specified text only if the request originates from XMLHttpRequest. @request is an instance variable that is available inside the ActionController class. Its xml_http_request?( ) method returns true if the request includes an X-Requested-With header containing the value XMLHttpRequest.
If the if condition returns false in the code sample, the controller object displays a view with the action name increment. In other words, it looks for <web-app-root>/views/hacks/increment.rhtml. If this view is not available, Rails raises an exception along the lines of "template increment.rhtml missing."
If you use Ajax a lot with Rails, you will find that a number of actions are defined solely for Ajax-related requests. This technique is a handy way to ensure that requests that don't originate from the request object are rejected.