Consuming SharePoint Lists via AJAX

VSeWSSFeatures_large_ch9[1] I’ve done a bit of work with customers interested in consuming SharePoint data without using the SharePoint web services.  A common request asks how to consume the data from JavaScript.

Turns out the answer is easy when you develop a custom feature.

I created a companion screencast on Channel9 that walks through the entire process of creating a feature using VSeWSS.  If you want to see all the details or think I didn’t detail a step enough in this post, please go watch the video since I create pretty much the entire thing from scratch.  This blog post is meant to make it easier for you to read the code and copy/paste to try it on your own.

Create the Feature

Using Visual Studio Extensions for Windows SharePoint Services 1.3 (current version is the VSeWSS 1.3 March 2009 CTP), create a new project and add a new feature (do this on the WSPView pane). 

image

Make sure to check the checkbox to add the default element.xml in the resulting dialog.  Once created, you are probably going to want to rename the feature from its default “Feature1” to something more maintainable, like “ListToJson”.  Just click the “feature1” node in the WSPView, hit F2, and rename it.  In the WSPView, find the feature.xml file and open it.  Give it a name, description, and the path to the image that you want to use.

 <?xml version="1.0" encoding="utf-8"?>
<Feature Id="16522953-a146-4110-b9ce-0e70c0f4218c"
         Title="List To JSON"
         Descriptio="Exposes a list using JSON data format"
         ImageUrl="ListToJson/json160.gif"
         Scope="Web"
         Version="1.0.0.0"
         Hidden="FALSE"
         DefaultResourceFile="core"
         xmlns="https://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="Element1\Element1.xml" />
  </ElementManifests>
</Feature>

To provide the image as part of my feature, I went to the Solution Explorer pane in Visual Studio 2008 and added a new “Template” project item.

image

Delete the generated TemplateFile.txt, we don’t need it.  Under the newly created Templates folder, add a folder “IMAGES”, and under the IMAGES folder add a subfolder called “ListToJson”.  In the ListToJson folder, add the image.  This is how you deploy items to the folders under the "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE" directory.  We’ll do this a few times.

When we created the feature, a new file called “element.xml” was created.  Open that file, and under the Elements node add a child node “CustomAction”.

 <CustomAction
    Id="ListToJson"
    Description="Export list to JSON data format"
    RegistrationType="List"
    RequireSiteAdministrator="false"
    Title="List to JSON"     
    ImageUrl="_layouts/images/ListToJson/json160.gif"
    Sequence="1000"
    GroupId="ActionsMenu" 
    Location="Microsoft.SharePoint.StandardMenu">
    <UrlAction Url="{SiteUrl}/_layouts/ListToJson/ListToJsonHandler.ashx?ListID={ListId}"/>
  </CustomAction>

Notice the UrlAction here points to the ListID, that’s how we pass the ListID to our custom ASHX handler.  While we’re at it, let’s see the code for the .ASHX handler. 

Create the IHttpHandler

We need to provide the .ASHX file.  Add a new folder under the “TEMPLATES” directory in your Visual Studio solution called “LAYOUTS”, and under the LAYOUTS folder add a child folder “ListToJson”.  Inside that folder, add a new file and rename it to “ListToJsonHandler.ashx”.  Inside the file, you’ll find there’s nothing to it except the 5-part name for the assembly and type that implements the IHttpHandler interface.

 <%@ WebHandler Class="ListToJsonHandler, Channel9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=df0b6232e0609274" Language="C#" %>

In your Visual Studio project, add a new class called ListToJsonHandler (not in a namespace, or you need to change the .ASHX above to reflect the proper type name).  It’s the same old IHttpHandler you’ve been coding for years, I just used some C# goodness in there for the property initializers and the yield statement in the iterator, as well as creating an extension method for the SPList type.

 using System;
using System.Web;
using Microsoft.SharePoint;
using System.Collections;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.IO;


public class ListToJsonHandler : 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)
    {
        string listIDFromUrl = context.Request.QueryString["ListID"];
        if (!string.IsNullOrEmpty(listIDFromUrl))
        {
            StringWriter writer = new StringWriter();
            HttpContext.Current.Server.UrlDecode(listIDFromUrl, writer); 
            Guid listID = new Guid(writer.ToString());

            SPList list = SPContext.Current.Web.Lists[listID];
            context.Response.Clear();
            context.Response.ContentType = "application/json";
            context.Response.Write(list.ToJson());
        }
    }
}

public static class MyExtensions
{

    public static string ToJson(this SPList list)
    {
        IEnumerable<ListItem> items = GetListItems(list);
        JavaScriptSerializer ser = new JavaScriptSerializer();
        return ser.Serialize(items);
    }

    private static IEnumerable<ListItem> GetListItems(SPList list)
    {
        foreach (SPListItem item in list.Items)
        {
            yield return new ListItem { Name = item.Name, Url = item.Url };
        }
    }
}
public class ListItem
{
    public string Name { get; set; }
    public string Url { get; set; }
}

The PublicKeyToken is not going to be correct in your environment, you need to use the Strong Name tool in the SDK to find the proper token.  My project is called “Channel9”, so I went to the bin/debug directory and ran the following command using the Visual Studio 2008 command prompt.

 sn.exe -Tp Channel9.dll

Copy the value of the public key token and paste it in the 5-part name in the .ASHX file above.  You now have all the pieces/parts to make this work.  Add a reference to the System.Web.Extensions.dll assembly, and everything should compile and deploy just fine.  You should now be able to run it and see your new feature added to the Actions menu for all lists on your site, and when you click it you should see the JSON representation of the data for the list.

Create the Application Page

This is the part that usually stumps ASP.NET devs new to SharePoint for the first time.  It’s easy to create a SharePoint application page using your existing ASP.NET skillz.  Under the TEMPLATES/LAYOUTS/ListToJson folder in your Visual Studio 2008 solution, add a new file “MyAppPage.aspx”.  The contents of that page are listed here.

 <%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>

<script runat="server">
    protected override void OnLoad(EventArgs e)
    {
        this.listID.Value = Request.QueryString["ListID"];         
    }
</script>
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderMain">

     <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="MyAppPage.aspx.js" />
        </Scripts>
    </asp:ScriptManager>

    <label title="Enter the ListID"></label>
    <input type="text" id="listID"  runat="server" />
    <input type="button" onclick="doit();return false;" value="Get the data" />
    <div id="result"></div>
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
    Query a list using JSON
</asp:Content>

See, it’s the same ASP.NET you’ve always coded.  A few things to note are the inclusion of the SharePoint assembly reference, and inheriting from the LayoutsPageBase type.  This will incorporate your page into the SharePoint framework, integrating security and the rest of the goodness.

That page uses a JavaScript file.  I use the ASP.NET AJAX 1.0 capabilities that are included out of box with Visual Studio 2008, I get this for free because I used the asp:ScriptManager control in my application page.

 function doit()
{
    $get("result").innerHTML = "";
    var req = new Sys.Net.WebRequest();
    var listID = $get("ctl00_PlaceHolderMain_listID").value;
    req.set_url("ListToJsonHandler.ashx?ListID=" + listID);
    req.set_httpVerb("GET");
    req.add_completed(OnRequestCompleted);
    req.invoke();
}

function OnRequestCompleted(sender, e)
{    
    var result = eval(sender.get_responseData());
    for (var i = 0; i < result.length; i++)
    {
        $get("result").innerHTML += result[i].Name + "&nbsp;" + result[i].Url + "<br/>";
    }
}

Since we added the new application page, we want to provide navigation so that we can actually access it.  Update the elements.xml file to include a new custom action.

   <CustomAction
    Id="ListToJSONPage"
    Title="List to JSON Page"
    Description="Queries the list data using AJAX"
    ImageUrl="_layouts/images/ListToJson/json160.gif"
    RequireSiteAdministrator="false"
    RegistrationType="List"
    GroupId="ActionsMenu"
    Sequence="1001"
    Location="Microsoft.SharePoint.StandardMenu">
    <UrlAction Url="{SiteUrl}/_layouts/ListToJSON/MyAppPage.aspx?ListID={ListId}"/>
  </CustomAction>

The last bit of magic here is to configure SharePoint to understand the .NET 3.5 assemblies.  Jan Tielens has a great post that shows how to enable .NET 3.5 in SharePoint, the lazy way.  Once you have your SharePoint site able to talk to the .NET 3.5 assemblies, you should be golden.  The result is a new item on the ActionsMenu for the lists on your site to expose them as JSON.

image

Clicking the link will take you to an application page with a textbox and a button,  the textbox already contains the GUID for the list that you want to query.  Clicking the button will show the data from the list.

image

 

For More Information

Screencast that shows all the steps to create the ListToJson feature for this post on Channel9

VSeWSS 1.3 March CTP Download

How to Enable .NET 3.5 in SharePoint 2007 Sites, The Lazy Way

Features for SharePoint, by Ted Pattison

How To: Add Actions to the User Interface