Friday, 5 November 2010

SP2010 AJAX part 3– using jQuery AJAX with a HTTP handler

  1. Boiling jQuery down to the essentials (technique)
  2. Using the JavaScript Client OM to work with lists (technique)
  3. Using jQuery AJAX with a HTTP handler (technique) - this article
  4. Returning JSON from a HTTP handler (technique)
  5. Enable Intellisense for Client OM and jQuery (tip)
  6. Debugging jQuery/JavaScript (tip)
  7. Useful tools when building AJAX applications (tip)
  8. Migrating existing applications to jQuery/AJAX

So far we’ve looked at jQuery for page manipulation and using the Client OM for talking to SharePoint. Today I want to talk about something else, and I think this is possibly the most important post in this series. jQuery and the Client OM are unlikely to deal with every scenario the AJAX-minded SharePoint developer will need to deal with. To be sure, the Client OM is surprisingly extensive and deals with way more than just fetching items from lists – that said, it’s not a full mirror of the server API and in any case, when writing server code which talks to client code (e.g. JavaScript)  sometimes you need full control over both sides of the fence. So, how do you build an AJAX-style  SharePoint application without using the Client OM (for whatever reason)?

Let’s consider the server side first - there are a couple of options: 

  • Write a WCF service (or indeed, ‘classic’ .asmx web service)
  • Write a HTTP handler
  • Write some other form of server code which (like the others) generates a response over HTTP
    • e.g. technically a good old .aspx page could work as the server-side component. However, this is a bad choice compared to the other options for a few reasons (mentioned shortly)

Arguably the purist route would be to develop a WCF service – indeed SharePoint 2010’s implementation of the Client OM is a WCF service. If you’re experienced with WCF, this is probably a good choice for you. However, my view is that a HTTP handler is a hundred times simpler, and that the features of WCF (e.g. transport flexibility, ability to pass large files, security etc.) are typically not necessary in an “internal to the farm” SP2010/AJAX app. Of course, if you’re building a public API to expose stock quotes to the world then things might be different. Alternatively, an esoteric approach could be to use something like a regular .aspx page – however an ASP.Net web forms page (i.e. not MVC) will be less efficient here as the page will go through the full ASP.Net lifecycle (Init, OnPreRender, Render etc.) despite the fact you probably don’t have any ASP.Net controls on the page. In other words we are using ASP.Net pages for something other than what they were designed.

So, a HTTP handler is a simple and effective means of  building an AJAX-style app. To some folks it’s a new technique, and for others it’s old news – but it’s my view that this approach is THE key to unlocking the ability to write AJAX apps, whether that’s on top of SharePoint or plain .Net.

The great thing about this technique is that the possibilities are unlimited – you can AJAX-ify anything, since you would write the C# code in the handler, then just call it from jQuery. Clearly this cannot be said about a ‘provided’ API such as the Client OM. By the way, I’d be interested to hear opposing opinions on the WCF vs. HTTP handler point.

In terms of the client, if we are talking about JavaScript (as we are in this series), then we need a way of calling ‘HTTP resources’ like those mentioned above from a JavaScript method. This has been possible for years, but the advent of jQuery means it’s way simpler than before – jQuery provides a handful of AJAX methods to call a server resource by URL, and the response is passed back to the JavaScript. Once you have the value from the server call (more on this later), you can display feedback to your user without reloading the page by simply using the methods described in part 1 (essential jQuery methods for updating pages). The jQuery AJAX methods are:

$.get() Requests a URL using a HTTP GET – calls .ajax() underneath
$.post() Requests a URL using a HTTP POST – calls .ajax() underneath
$.ajax() Allows full control – exposes full set of parameters
$.getJson() Requests a URL GET and parses response for JSON format using $.parseJSON

Creating a HTTP handler (server)

So, we’ve hopefully established that a HTTP handler combined with jQuery’s AJAX methods is a powerful technique – now let’s look at the detail.

HTTP handler

A HTTP handler is a .Net class which implements System.Web.IHttpHandler – the ProcessRequest() method is the main method. Here, you write code which determines what is returned when the URL for the handler is requested. So where a URL to an .aspx page would return HTML, you can return whatever you want – a simple string, some more complex XML, or perhaps JSON (the next article in this series). A simple handler returning a string looks like this:

using System;
using System.Web;
 
namespace COB.SPSaturday.Demos.Handlers
{
    public class DemoHandler : IHttpHandler
    {
        public bool IsReusable
        {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get { return true; }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("From the handler at " + DateTime.Now);
        }
    }
}

The .Net framework needs to know about such a handler before it can be used. Two options here – either associate an .ashx file with your .cs file above (the handler is called by the path to the .ashx in this scenario), or add a web.config entry telling .Net how to route the request to your class (the handler is called by whatever path you define in web.config in this scenario).

Using an .ashx

Create a file in your project with an .ashx extension (there is no VS item template, unless I keep missing it), and use the WebHandler directive to point to your implementation. This works because .ashx is a special extension where .Net knows to resolve the class by looking for the WebHandler directive:

<%@ Assembly Name="COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" %>
<%@ WebHandler Language="C#" Class="COB.SPSaturday.Demos.Handlers.DemoHandler" CodeBehind="DemoHandler.cs" %>
Using a web.config entry

Add an entry (e.g. via SPWebConfigModification) like this – notice you can specify a custom path/extension if that’s preferable for any reason:

<handlers>
    <!-- other handlers here -->
    <add name="DemoHandler" path="/_layouts/COB/DemoHandler.cob" verb="*" type="COB.SPSaturday.Demos.Handlers.DemoHandler, COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" />
</handlers>

Most folks opt for the .ashx route since it avoids defining something in web.config for every handler your application uses.

Calling a HTTP handler (client)

Earlier, we detailed jQuery’s four core methods for making a request to the server. Here’s an example (using the .get() method):

<fieldset id="fldDemo1">
    <legend>Demo 1 - simple handler</legend>
    <div id="demo1Row" class="demoRow">
            <div class="demoControls">
                <button id="btnDemo1" type="button">Call handler</button>
            </div>
            <div class="demoResults">
                <span id="demo1Result" />
            </div>
            <div class="clearer" />
    </div>
    </fieldset>
    <script type="text/javascript">
    $('#btnDemo1').click(function () {
        $.get('/_layouts/COB/DemoHandler.cob',
            function (data) {
                $('#demo1Result').html(data);
            });
    });
    </script>

As you can see, the key is taking the data property which is passed from the server to the jQuery callback, and using jQuery’s ability to manipulate the page with methods like .html(). Hey presto, you can now do anything you like without a full postback!

Something to note is that if you’re modifying data in the handler, a good practice is to make it a .post() request rather than .get() – of course, SharePoint disallows updates on a GET request by default so you must do something to work around this anyway (the ghetto alternative being to set SPWeb.AllowUnsafeUpdates = true).

Putting it into practice

Now that we’ve learnt the mechanisms, consider how usage might pan out in the real-world:

  • Return data:
    • If you’re returning unstructured data (e.g. a string), things are pretty simple (N.B. another jQuery AJAX method I omitted to mention is $.load() method which simplifies things further)
    • For structured data (e.g. objects), this is often returned as a JSON-serialized string – commonly preferred to XML, but there are plenty of samples out there for XML too
    • Because setting the HTML of a page element is so easy, it’s tempting to have a handler return a huge string of hardcoded HTML (e.g. when we’re updating a big complex area of the page) – indeed I mentioned this as a “SP2010/jQuery/AJAX survivor’s technique” in my SharePoint Saturday talk. This works fine but a far better approach is to use the new jQuery templates capability – Jan Tielen’s Getting Started with jQuery Templates and SharePoint 2010 is a great post.
  • Factoring
    • A good way to structure your code is to have a handler implement multiple methods (e.g. for related functionality), and pass a querystring parameter in the AJAX request to indicate which method to execute. You certainly don’t need a handler for every method you might implement.

Next time - Returning JSON from a HTTP handler

9 comments:

Venkatesh Janaga said...

hi,

I want to display flexigrid with jquery.

I am able to display flexigrid with webservice.

but i want to use HttpHandler instead of Webservie.

here is my jquery code.

if i use url: 'GetProductsHandler.asmx/GetProductList' means its working ..

but the same thing i use for httphandler its giving error.


please help me in this isue.



$(document).ready(new function () {
// alert("hi");
$("#fgrdProduct").flexigrid
(
{

url: 'GetProductsHandler.ashx/GetProductList',
//url: 'FlexGridWithXMLDoc/GetProductList',
dataType: 'xml',
colModel: [
{ display: 'Id', name: 'Id', width: 20, sortable: true, align: 'left' },
{ display: 'Name', name: 'Name', width: 180, sortable: true, align: 'left' },
{ display: 'Description', name: 'Description', width: 180, sortable: true, align: 'left' },
{ display: 'Unit', name: 'Unit', width: 120, sortable: true, align: 'left' },
{ display: 'Unit Price', name: 'UnitPrice', width: 130, sortable: true, align: 'left', hide: false },
{ display: 'Create Date', name: 'CreateDate', width: 80, sortable: true, align: 'left' }
],
searchitems: [
{ display: 'Name', name: 'Name' },
{ display: 'Description', name: 'Description' },
{ display: 'Unit', name: 'Unit' },
{ display: 'Unit Price', name: 'UnitPrice' },
{ display: 'Create Date', name: 'CreateDate' },
{ display: 'Id', name: 'Id', isdefault: true }
],
sortname: "Name",
sortorder: "asc",
usepager: true,
title: 'List of Products',
useRp: true,
rp: 10,
showTableToggleBtn: true,
width: 805,
onSubmit: addFormData,
height: 200
}
);

//This function adds paramaters to the post of flexigrid. You can add a verification as well can
//return false if you don't want flexigrid to submit
function addFormData() {

//passing a form object to serializeArray will get the valid data from all the objects, but, if you pass a non-form object,
//you have to specify the input elements that the data will come from
var dt = $('#sform').serializeArray();
$("#fgrdProduct").flexOptions({ params: dt });
return true;
}

$('#sform').submit
(
function () {
$('#fgrdProduct').flexOptions({ newp: 1 }).flexReload();
return false;
}
);

});

Chris O'Brien said...

@Venkatesh,

Sorry, that's very specific to the flexigrid component you're using (I presume it's a jQuery plugin?) -I've never used this, so can't comment. The creators of the plugin should be able to help with your question though.

Good luck,

Chris.

emzero said...

Where did you get the "/_layouts/COB/DemoHandler.cob" url in the jquery request?
Shouldn't be .ashx? I have done it and I don't have any .cob of course. I have the .ashx, but I'm not sure how to build the correct path to call it from jQuery.

Can you give me more info about it?

Thanks! Great article!

Chris O'Brien said...

@emzero,

In this example I'm just showing how your handler doesn't have to be a .ashx extension (e.g. how Microsoft implement certain services within SharePoint 2010). You need to the web.config entry as described in the section above where you saw the call to the .cob URL.

As I conclude, .ashx is easier since it's hooked up for you with no need for web.config entry.

HTH,

Chris.

Rogier said...

@emzero @COB:

I also went the ashx way, and sorry Chris, but you're missing quite a few explanation steps, this site is truly step by step on how to use it:

http://www.lifeonplanetgroove.com/blog/index.php/2010/10/15/adding-and-deploying-generic-handlers-ashx-to-a-sharepoint-2010-visual-studio-project/

Also, this other site explains how to use tokens, so you can replace

<%@ Assembly Name="COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" %>

with:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>

Chris O'Brien said...

@Rogier,

Thanks - useful reference for those needing more detail on HTTP handlers. Nice idea to enable token replacement for those files too.

Cheers,

Chris.

Frederic Latour said...

Hi,
You say that aspx involves the full page Life cycle but that's not true if you are using web page methods as part of your aspx code behind.
I'm curious to know the pro and cons of either approach (ashx vs aspx web page methods) if anybody knows.
As far as I can think of, the aspx approach seems marginally easier on a deployment standpoint.

Chris O'Brien said...

@Frederic,

Very true. However, is it just me or are ASP.Net page methods a niche technique which passed most developers by? For me, many of the things I call using AJAX are core CRUD methods which go through a service layer/DAL, and I wouldn't want the logic (even the front layer) tied to any particular page.

A handler hasn't been difficult for us to deploy, it just goes into a subfolder within the _Layouts directory.

I'd also be interested in any arguments in favour of page methods though. Here's a reference for anyone who'd like to know more about ASP.Net page methods http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/

Thanks for the comment,

Chris.

Brian Driscoll said...

The item template for ashx files is Generic Handler, in the Web item group.