Routing Only AJAX Requests In RoR

Don’t Solve The Problem, Prevent It.

When you deal with input in programming, it is always a good practice to make sure you can’t get from the user what you wouldn’t want to get. I know it sounds strange at the beginning, but let me further explain with the simplest example:

Let’s say you want to get a date as an input from the user. A date can have many different formats and dealing with every possible format could become very frustrating. Now if you let the user enter the date in a simple text box you leave the power in the hands of the user. He decides how the input gets to your server, and you find yourself wasting a lot of code lines on the parsing of the date.

That is exactly the reason why graphical date picker components were created. A date picker lets the user choose from a graphical calendar and injects the formatted selection into the text box, which, obviously, should not be editable. So instead of allowing a broad number of date formats and dealing with the input in our code, we narrowed the input format to exactly what we want to get and we can deal now with business logic instead of date parsing. In other words, we did not solve the problem, we prevented it from ever happening. That’s a great attitude which will spare you a lot of code lines.

ActionDispatch and Problem Prevention

In this context, I wanted to talk about Rails routing a little bit. When we think about it, the ActionDispatch mechanism which resolves routes to controller actions is your (as a programmer) first encounter with user input. We already agreed it is best to prevent a problem, than resolving it in our code, so the routing mechanism is definitely the best place to start looking for code smells.

Let’s take a simple example in which we have a Post model with title, content, author_name and post_number attributes. We also want to let the user filter posts by each of these attributes at /posts/filter/content/<some_content_filter>. One might think of following solution :

Although this one will do the job, what we are doing here is solving the problem instead of preventing it. There is no reason, what so ever, to go through a full stack response and not halt the processing of the request the earliest possible – our routing definitions:

This will respond with 404 not found to any filter which does not match the regular expression we defined. A request with an illegal filter won’t even reach our controller code – ActionDispatch deals with it a lot earlier. We benefit both in performance and in less code clutter in our controller.

Correctly Routing Only AJAX Requests

This was a basic example of using the routing mechanism in order to filter unwanted requests.
A more complicated one would be to make some controller actions respond only to AJAX requests. One way to do it would be the following:

respond_to :js and respond_with @posts make sure our controller will only reply to AJAX requests. I think you know where I’m going now – why not stop the request processing with a routing definition?
An ideal solution could be:

Now there are some good news and some bad news regarding this code snippet:
The bad news: The :constraints hash does not have a :only_ajax => true option.
The good news: It is very easy to implement one yourself:

All that is needed is an object which responds to the matches? method. The Request object is passed as a parameter to it so you can query it for whatever you’d like, including it being an xhr request using the xhr? method.

This way non-AJAX requests won’t even reach your controller, and you don’t have to deal with scary
respond_to do |format| blocks.

About these ads

, , ,

  1. #1 by Wattswing on April 14, 2014 - 8:03 am

    How about
    match ‘posts/filter/:by_attribute/:value’ => ‘posts#filter’,
    :constraints => -> (req) { req.xhr? }

    Don’t like to see non-route code into the routes file. :p
    And you are instantiating a new OnlyAjaxRequest class every time you declare a new full_ajax route, isn’t it ? Why not using Rails route helpers ?

    Thanks for the article, nice approach.

    • #2 by Erez Rabih on April 14, 2014 - 10:25 am

      Thanks for the comment I have a few thoughts on it:

      1. I was actually not aware of the -> (req) { req.xhr? } syntax to filter only ajax requests. It is really cool thanks for mentioning this up.
      2. Regarding mixing non-route code: The right thing to do is to put this code under lib/routes or something like that and have all routes contraints in there.
      3. Also, if you find yourself repeating on that -> (req) { req.xhr? } I believe my approach is much more dry.
      4. I really think that my approach is more object oriented, readable and reusable than just putting this lambda syntax on the routes.rb file.

  2. #3 by Gary on November 21, 2013 - 11:59 am

    Lovely approach, thanks :)

  3. #4 by Gastón Sánchez on April 5, 2013 - 3:59 pm

    Great! very neat solution. Thanks.

  4. #5 by SutoCom on October 15, 2012 - 4:15 pm

  5. #6 by sygendev on October 9, 2012 - 7:59 am

    Reblogged this on SyGen development blog and commented:
    Burn your controller a la Erez Rabih

  6. #7 by sygendev on October 9, 2012 - 7:59 am

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Get every new post delivered to your Inbox.

%d bloggers like this: