Detecting Ajax Events on the Server

read 14 comments

When working with an Ajax-enhanced website, it's generally a good idea to provide a regular request fallback for any core functionality of the site. When you work to ensure that a fallback is in place, you will be faced with determining when a particular request is an Ajax request or just a normal page request. In most situations, it's considered a best practice to build your site without Ajax first, adding the Ajax functionality afterward so the fallbacks are in place from the beginning.

Fortunately, jQuery makes it super easy to differentiate the Ajax requests from normal page views.

X-Requested-With Header

If you were to browse the jQuery source, you would see that jQuery adds a special HTTP header to (almost) every Ajax request it makes with $.ajax(), $.get(), $.getJSON(), $.post() and .load() methods.

Continue Reading Below
JavaScript:
  1. if ( !remote ) {
  2.     xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  3. }

Keep in mind, though, that the header is not sent with $.getScript() or any JSONP request made with $.ajax() or $.getJSON(), since they aren't technically Ajax requests.

Detection on the Server

By checking for this header, you can easily determine on the server if the request was initiated by jQuery. Each server-side language and web server combination will give you different ways of accessing this header, but let's look at a few.

In PHP, you can use this function to determine the type of request:

PHP:
  1. function is_xhr() {
  2.   return @ $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] === 'XMLHttpRequest';
  3. }
  4.  
  5. if( is_xhr() ){
  6.   // respond to Ajax request
  7. } else {
  8.   // respond to normal request
  9. }

Sinatra and Ruby on Rails have special shortcuts already built in:

RUBY:
  1. if request.xhr?
  2.   # respond to Ajax request
  3. else
  4.   # respond to normal request
  5. end

Since other libraries outside of jQuery also set the X-Requested-With header, many server languages have special support for reading it. Check the documentation for your favorite server language to see if it has any convenience methods for detecting an Ajax request.

A Small Example

Ok, let's apply this knowledge to add Ajax to a simple email signup form. Here is our existing HTML:

HTML:
  1. <form id="email_form" action="email.php" method="POST">
  2.   <p>
  3.     <label for="email">Email Address: </label><br />
  4.  
  5.     <input type="text" name="email" id="email" value="" />
  6.   </p>
  7.   <p>
  8.     <input type="submit" value="Subscribe" />
  9.   </p>
  10. </form>

And here is the existing email.php (Note: on a normal error you might redirect back to show a message, or render the form again. This is for example only):

PHP:
  1. <?php
  2.    $email = trim( @$_POST['email'] );
  3.  
  4.    // Call our special email subscribe function
  5.    $success  = subscribe( $email );
  6. ?>
  7. <!DOCTYPE html>
  8. <html>
  9.   <head>
  10.  
  11.      <title>Email Signup</title>
  12.      <style type="text/css">
  13.         .success { color: green }
  14.         .error   { color: red }
  15.      </style>
  16.   </head>
  17.   <body>
  18.  
  19.     <?php if( $success ): ?>
  20.       <div class="success">Your Signup Is Successful! Thank you!</div>
  21.     <?php else: ?>
  22.       <div class="error">Something went wrong.
  23.             Please click your browser's back button and try again.</div>
  24.     <?php endif; ?>
  25.  
  26.   </body>
  27. </html>

The previous two pages work together to provide a simple signup functionality, without JS or Ajax involvement.

Now let's add the Ajax. We’ll add the following to the form page:

JavaScript:
  1. <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
  2.  
  3. <script type="text/javascript" charset="utf-8">
  4.   jQuery(document).ready(function($) {
  5.    
  6.     $("#email_form")
  7.         .after("<div id='notice'></div>")
  8.         .submit(function(e){
  9.       // Cancel the normal submission of the form
  10.       e.preventDefault();
  11.  
  12.       var email = $("#email").val();
  13.  
  14.       // perform validation; not shown
  15.  
  16.       // Make our request and expect JSON in return
  17.       $.post( 'email.php', { email: email }, function(data){
  18.         if( data.success ){
  19.           $("#notice").html("<div id='success'>Your Signup Is Successful! Thank you!</div>");
  20.         } else {
  21.           $("#notice").html("<div id='error'>There was an error. Please make sure your email is valid and try again.</div>");
  22.         }
  23.       }, 'json');
  24.     });
  25.   });
  26. </script>

Finally, we replace the initial PHP code block in our email.php file to take advantage of the Ajax submission:

PHP:
  1. <?php
  2.   function is_xhr() {
  3.     return @ $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] === 'XMLHttpRequest';
  4.   }
  5.  
  6.   $email = trim( $_POST['email'] );
  7.  
  8.   // Call our special email subscribe function
  9.   $success  = subscribe( $email );
  10.  
  11.   if( is_xhr() ){
  12.     // Not explicitly needed, but we like being accurate, right?:
  13.     header('Content-type: application/json');
  14.     if( $success ){
  15.       echo json_encode( array( 'success' => true ) );
  16.     }  else {
  17.       echo json_encode( array( 'error' => true ) );
  18.     }
  19.     exit(); // We don't need to render anything else
  20.   }
  21.  
  22.   // ... the rest of our original PHP page

Conclusion

That's it! A few lines of code, and your server side code can now render different content depending on how the request was generated. Now that you know how to detect jQuery Ajax events, you no longer need to resort to passing ajax: true as data on your requests or calling a separate file altogether.


comment feed

14 comments

  1. Aaron Mc Adam

    Hey, thanks for this! I've been passing "{ ajax : true }" whilst developing my project app. This will save me a bit of hassle :)

    • If you’re ardealy sold on the idea of promises, you might consider using this library I ported from Tyler Close’s Waterken project in conjunction with the Google Caja team. It’s a very well-designed promise API that’s getting some traction in the CommonJS community. I also understand there might ardealy be efforts within Mozilla to build the Node API within Spidermonkey instead of V8. There’s also GPSEE, a CommonJS implementation, which Wes Garland mentioned he was getting to work with the Node API’s as well.

  2. Jon

    Likewise, I've also been passing a boolean. I'll change that now!

  3. Doug Neiner

    Well @Jon and @Aaron I am glad the article helped you out! Its the little things like this that go a long way in keeping code clean and readable.

  4. Mark Kumar

    Thank you soo much for sharing this. I'm new to jQuery and Ajax. So, I have been looking to something like this tutorial and your is very informative and to the point. Thanks for sharing. I will definitely use this on my site: http://www.kumarphotography.com.

    Mark Kumar

  5. bernard

    I do not really like how the PHP and JS duplicate themselves creating success or failure html messages. Say, you need to update the message, or the markup. You need to do that twice. I understand you want to keep the examples simple, but I'm missing here a note, that this is not the best approach (pointing people who want to learn eg. to Resig's templating). After all, this blog is about learning good practices :)

  6. Django has a similar feature: is_ajax(), that is a method of HttpRequest.
    For example in a view:

    
    def my_view(request):
       if request.is_ajax():
          # respond to Ajax request
       else:
          # respond to normal request
    

    Great article!

  7. This is great, thanks for posting such a nice article. Actually, it looks like the whole site is filled with jquery gold.. awesome!

  8. Using @ to suppress errors is slow and probably the wrong thing to use in this context. Might wanna use isset() instead:

    
    function is_xhr() {
      return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
    }
    
    • Well typed, I was just about to write the same thing!

      On a similar note,
      $email = trim( $_POST['email'] );
      could be changed to
      $email = isset( $_POST['email'] )? trim( $_POST['email'] ): '';

  9. es

    The jQuery form plugin doesn't send XMLHttpRequest header when an upload is posted because of using an iframe for file upload.

  10. Gabriel Hernandez

    Hi there,
    Thanks a lot for your post. I've noticed a problem and perhaps you know the solution??? IE7 is not setting the variable $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] so when I look for that variable in PHP, in IE7 it cannot be found!!. The XHR request status is set to 200 -success-, but the variable 'HTTP_X_REQUESTED_WITH' has not been set!. this is only when using IE 7.

    Any help is appreciated!.

  11. I prefer this:

    
    $isXHR = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 
        strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])==='xmlhttprequest';
    

4 Pings

  1. [...] 仅仅几行代码,搞定了。 原文链接 Detecting Ajax Events on the Server [...]

  2. Detecting Ajax Events on the Server - Eureka Seven

    [...] via highlight_lines Tags: Ajax, JavaScript, jQuery, Memo Comments RSS feed [...]

  3. [...] im very impressed, especially when people talk about how easy XMLHTTPREQUEST processing and shortcuts can be, and how easy  it is to apply across [...]

  4. How To Build A Real-Time Commenting System

    [...] AJAX request and maintain our basic functionality if it isn’t (for more information on this see Detected AJAX events on the Server). We also define two handlers; postSuccess for handling the comment being successfully stored and [...]

Sorry, but comments for this entry are now closed.