This post is part of a series of related posts on how to use Windows Live Messenger Connect.
1. Using Messenger Connect REST Explorer for exploring Windows Live resources.
2. Using the Messenger Connect JavaScript API for listing your contacts.
3. (this post) Getting contacts in three different ways (API and REST).
Overview

This post compares side-by-side three methods of getting a resource (in this case we stick with contacts):
1. Using the JavaScript API
- Pros: API does all the hard work for you; no cross browser issues.
- Cons: You have to load a library.
2. Using the REST endpoint directly and getting data with a jQuery.ajax call.
- Pros: Stays all in JavaScript; jQuery makes it easy.
- Cons: Cross-browser issues (to work in IE you must enable cross domain posts, other browsers?).
3. Using the REST endpoint directly and getting data with an HttpWebRequest in the page code-behind.
- Pros: Can use LINQ to process response; no cross-browser issues.
- Cons: You have to have a code-behind, a server-side page.*
* To be fair, approach 1 requires a code file that implements a session handler for unsecured (non HTTPS) scenarios. See the Messenger Connect JavaScript API post for details on that.
Method 1 is probably going to be what you'll use most of the time, but it is interesting to compare the other approaches. To that end, this post shows an ASPX and code-behind page that shows these three approaches for getting the first five contacts in your contact list. It is built on the Windows Live Application template which can be downloaded here and of course requires that you are registered as a Messenger Connect application. Again, all is explained here.
Update 2011-02-13: Here's a zip of the ASPX and code-behind page.
Without further ado, here's the code (a thorough explanation follows):
The Page Markup
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ContactsComparison.aspx.cs"
Inherits="WindowsLiveWebApplicationBeta.ContactsComparison" %>
<%@ Import Namespace="System.Web.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wl="http://apis.live.net/js/2010">
<head id="Head1" runat="server">
<title>Get Contacts Comparison</title>
<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>
<script type="text/javascript">
var dvContactList;
var dataContext;
var contactsView;
var totalNumContacts;
var displayNumContacts = 5;
var cid;
var accessToken;
var uriContactGet = "http://apis.live.net/V4.1/cid-{0}/Contacts/AllContacts";
jQuery.noConflict();
function appLoaded(applicationLoadCompletedEventArgs) {
}
function SignedOutCallback() {
}
function SignedInCallback(signInCompletedEventArgs) {
if (signInCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
log("Failed to sign in: " + signInCompletedEventArgs.get_resultCode());
return;
}
else {
auth = Microsoft.Live.App.get_auth();
accessToken = auth.get_accessToken();
cid = auth.get_cid();
}
}
// *** Using JavaScript API *** //
function getContactsUsingAPI() {
if (Microsoft.Live.App.get_auth().get_state() === Microsoft.Live.AuthState.authenticated) {
dataContext = Microsoft.Live.App.get_dataContext();
dataContext.loadAll(Microsoft.Live.DataType.contacts, contactsLoaded);
}
else {
log("Failed to get contacts using API. Are you authenticated?");
}
}
function contactsLoaded(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Contacts failed to load...!");
return;
}
contactsCollection = dataLoadCompletedEventArgs.get_data();
totalNumContacts = contactsCollection.items.length;
for (var i = 0; i < Math.min(totalNumContacts, displayNumContacts); i++) {
var contact = contactsCollection.getItem(i);
addItemToList("ContactsAPI", contact.get_firstName() + " " + contact.get_lastName());
}
}
// *** Using REST with jQuery *** //
function getContactsUsingJQuery() {
if (Microsoft.Live.App.get_auth().get_state() === Microsoft.Live.AuthState.authenticated) {
try {
jQuery.ajax({
url: uriContactGet.replace('{0}', cid) + "?$top=" + displayNumContacts,
type: "GET",
async: true,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "WRAP access_token=" + accessToken);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
},
success: function (data, status, xhr) {
var contactsCollection = data.Entries;
for (var i = 0; i < Math.min(contactsCollection.length, displayNumContacts); i++) {
if (contactsCollection[i] !== undefined) {
var contact = contactsCollection[i];
addItemToList("ContactsRESTJQuery", contact.FirstName + " " + contact.LastName);
}
}
},
complete: function (xhr, status) {
var response = status "no response text";
log("Ajax request completed with " + response + ".");
},
ajaxError: function (data) {
var response = data "no response text";
log("Failure: " + response);
},
error: function (xhr, status, err) { log('Exception: ' + status + ', status = ' + xhr.status + ', message = ' + xhr.statusText); }
});
}
catch (err) {
log("Could not complete a status request. Is cross domain access enabled? Message = " + err);
}
}
else {
log("Failed to get contacts using jQuery Ajax call. Are you authenticated?");
}
}
function addItemToList(parent, item) {
$get(parent).innerHTML += "<li>" + item + "</li>";
}
function log(msg) {
$get('Output').innerHTML += msg + ";";
}
</script>
<style type="text/css">
div.left { float: left; width: 300px; border: 1px solid blue; min-height: 250px; padding: 5px;}
div.output {clear: both;}
ul { list-style-type: none;}
</style>
</head>
<body>
<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Contacts.View, WL_Profiles.View"
onload="appLoaded">
</wl:app>
<form id="form1" runat="server">
<div>
<h1>
Getting Contacts - Comparing Three Different Approaches</h1>
<wl:signin onsignin="SignedInCallback" onsignout="SignedOutCallback" signedouttext="Sign In To Authenticate and Begin">
</wl:signin>
<div class="left">
<h4>Using the JavaScript API.</h4> <input type="button" value="Fetch" onclick="getContactsUsingAPI()" />
<ul id="ContactsAPI">
</ul>
</div>
<div class="left">
<h4>Using REST with jQuery Ajax request.</h4> <input type="button" value="Fetch" onclick="getContactsUsingJQuery()" />
<ul id="ContactsRESTJQuery">
</ul>
</div>
<div class="left">
<h4>Using REST with C# HttpWebRequest.</h4> <asp:Button ID="GetContactsButton"
runat="server" Text="Fetch" onclick="GetContactsButton_Click" />
<ul id="ContactsRESTCodeBehind" runat="server">
</ul>
</div>
<div class="output"><span id="Output" runat="server" /></div>
</div>
</form>
</body>
</html>
The Page Code-Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
// Additional using statements.
using System.Net;
using System.Web.Configuration;
using System.IO;
using Microsoft.Live;
using System.Xml.Linq;
namespace WindowsLiveWebApplicationBeta
{
public class AuthStoreProvider : Microsoft.Live.IAuthStoreProvider
{
public void StoreToken(HttpContext context, string userId, IDictionary<string, string> authInfo)
{
// Get and store the AccessToken.
AccessToken = authInfo["AccessToken"];
CID = authInfo["UID"];
}
public static string AccessToken { get; set; }
public static string CID { get; set; }
}
public partial class ContactsComparison : System.Web.UI.Page
{
public string SessionId
{
get
{
SessionIdProvider oauth = new SessionIdProvider();
return "wl_session_id=" + oauth.GetSessionId(HttpContext.Current);
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
string baseApplicationsEndpoint = "http://apis.live.net/V4.0/cid-{0}/Contacts/AllContacts";
string ApplicationsEndpoint = "";
string MaxNumToDisplay = "5";
string br = "<>";
// *** Using REST with HttpWebRequest *** //
protected void GetContactsButton_Click(object sender, EventArgs e)
{
ApplicationsEndpoint = baseApplicationsEndpoint.Replace("{0}", AuthStoreProvider.CID) + "?$top=" + MaxNumToDisplay;
try
{
// Initialize a request and define its characteristics.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ApplicationsEndpoint);
request.Method = "GET";
request.Accept = "application/atom+xml";
request.ContentType = "application/atom+xml";
// Get the AccessToken stored when the user signed in.
request.Headers.Add("Authorization", "WRAP access_token=" + AuthStoreProvider.AccessToken);
// Get the client Id from the web.config.
request.Headers.Add("X-HTTP-LiveFX-ApplicationId", WebConfigurationManager.AppSettings["wl_wrap_client_id"]);
// Send the request and get the response.
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
string responseText = streamReader.ReadToEnd();
CreateContactList(responseText);
streamReader.Close();
responseStream.Close();
}
catch (WebException Ex) // Catch is reached when the request is not successful.
{
HttpWebResponse response = (HttpWebResponse)Ex.Response;
Stream responseStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
string errorText = streamReader.ReadToEnd();
Output.InnerHtml += errorText;
Output.InnerHtml += br;
Output.InnerHtml += "Access Token = " + AuthStoreProvider.AccessToken + br;
Output.InnerHtml += "CID = " + AuthStoreProvider.CID + br;
streamReader.Close();
response.Close();
}
}
protected void CreateContactList(string input)
{
XNamespace ns = "http://www.w3.org/2005/Atom";
XNamespace nsp3 = "http://schemas.microsoft.com/ado/2007/08/dataservices"; // p3
TextReader tr = new StringReader(input);
XDocument xdoc = XDocument.Load(tr);
var contentQuery = from entry in xdoc.Descendants(ns + "content")
select entry;
foreach (XElement contentElement in contentQuery)
{
string firstName = contentElement.Descendants(nsp3 + "FirstName").FirstOrDefault().Value;
string lastName = contentElement.Descendants(nsp3 + "LastName").FirstOrDefault().Value;
ContactsRESTCodeBehind.InnerHtml += "<li>" + firstName + " " + lastName + "</li>";
}
}
}
}
Web.config key
<add key="wl_wrap_store_provider_type"
value="WindowsLiveWebApplicationBeta.AuthStoreProvider,WindowsLiveWebApplicationBeta"/>
Explanation of the code:
- Approach 1 (Javascript API) is all based off the loader (http://js.live.net/4.1/loader.js) which figures out what is needed for your page and enables you to work with a data context, ask for resources through the data context, and then work with the returned resource collection. Behind the scenes it is using the REST endpoints similarly to what is done in Approach 2 and 3.
- Approach 2 (REST with jQuery) depends on an endpoint for the resource you are interested in (e.g. http://apis.live.net/V4.1/cid-{0}/Contacts/AllContacts). The {0} value is replaced with the unique ID of the authenticated user Windows Live User. How is this endpoint URI known? You can figure it out from using the Windows Live Rest Explorer or you can look in the documentation, for example, for the AllContacts collection documentation page.
- Approach 2 requires the access token to be sent as a header as part of the AJAX request. The access token can be obtained from the Auth class.
- Approach 3 (REST in code-behind) also depends on an endpont for the resource. In this example it's defined in the code-behind similarly to Approach 2.
- Approach 3 as well depends on putting the access token into the request header. It's a bit more complicated in this case because you need to define a class (AuthStoreProvider in this code exampled) to implement the IAuthStoreProvider interface and get access to the access token. Finally, you need to make sure you hook up your AuthStoreProvider with an app key in the web.config file shown above.
A screenshot of the resulting page is at the beginning of this post. No surprises, each approach returns the same data! For more information on using LINQ with the Atom feed see this post. Finally, if you are using this in a web farm scenario you might consider storing the access token and cid in a cookie instead of using a static as shown here.






