SharePoint 2013 REST Services using C# and the HttpClient (for Windows Store Apps)

SharePoint Web Services have come a long way in the last few versions.  I have spent a lot of time working with the SharePoint Object Model And recently started to use the Client Object Model which are both wonderful libraries with great documentation and numerous articles about them.

However, if you have delved into mobile app development recently you will realize that Windows Store projects only give a very specific subset of the core .NET libraries and that Exchange Web Services and the SharePoint Client libraries and many others are not available.  Now this does introduce some difficulty but in SharePoint 2013 REST services will allow you to accomplish almost any task in SharePoint.  The problem is that Microsoft or the development community has decided that any of us working with REST services directly will need to use JavaScript, at least if we want any examples of what these requests would look like.  While I do not mind doing minor tasks in Jscript I vastly prefer C# for doing larger applications and after doing dozens of searches, reading over a hundred articles and posts, I finally broke down and reverse engineered some Jscript examples and massively reworked what few C# examples existed for SharePoint REST.

I will be showing four main things in this post.  How to create a digest, which is used in the headers of any REST query passed from the HttpClient for authentication.  I will demonstrate how to get a list of items and how to work with the Json Objects using only native Windows Store libraries.  I will demonstrate a file upload and finally how to create a new list item with a lookup column.

The Examples:

Creating your digest:

String retVal = "";
try
{
string url = "https://YourSite.com/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
string cmd = "_api/contextinfo";

client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("ContentLength", "0");
StringContent httpContent = new StringContent("");
var response = client.PostAsync(cmd, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
JsonObject val = JsonValue.Parse(content).GetObject();
JsonObject d = val.GetNamedObject("d");
JsonObject wi = d.GetNamedObject("GetContextWebInformation");
retVal = wi.GetNamedString("FormDigestValue");
}
}
catch
{ }
return retVal;

Getting a list of items

In this examples and all that follow the object digest would be passed from the return in the function above:
string url = "https://YourSite.com/Subsite/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");
StringContent strContent = new StringContent("");
HttpResponseMessage response = await client.PostAsync("_api/web/lists/GetByTitle('Your List Title')/GetItems(query=@v1)?@v1={'ViewXml':''}", strContent);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync();
JsonObject d = JsonValue.Parse(content.Result).GetObject();
JsonObject results = d["d"].GetObject();
JsonArray jobs = results["results"].GetArray();
foreach (JsonValue job in jobs)
{
JobBox.Items.Add(job.GetObject()["Job"].GetObject().GetNamedString("Label"));
}
}
else
{
var content = response.Content.ReadAsStringAsync();
}

Upload a File

FileOpenPicker picker = new FileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
picker.ViewMode = PickerViewMode.Thumbnail;
// Filter to include a sample subset of file types.
picker.FileTypeFilter.Clear();
picker.FileTypeFilter.Add(".bmp");
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".jpg");
// Open the file picker.
StorageFile path = await picker.PickSingleFileAsync();
if (path != null)
{
string url = "https://YourSite.com/Subsite/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");
client.DefaultRequestHeaders.Add("binaryStringRequestBody", "true");
IRandomAccessStream fileStream = await path.OpenAsync(FileAccessMode.Read);
var reader = new DataReader(fileStream.GetInputStreamAt(0));
await reader.LoadAsync((uint)fileStream.Size);
Byte[] content = new byte[fileStream.Size];
reader.ReadBytes(content);
ByteArrayContent file = new ByteArrayContent(content);
HttpResponseMessage response = await client.PostAsync("_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?@TargetLibrary='Project Photos'&@TargetFileName='TestUpload.jpg'", file);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{ }
}

Create List Item with a multivalue lookup field

string url = "https://YourSite.com/Subsite/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");
HttpContent content = new StringContent("{ '__metadata': { 'type': 'SP.Data.ReportListItem' }, 'Title': 'NewTitle', 'PhotosId': { 'results': [2] }, 'Details': 'Another successful day!' }");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
HttpResponseMessage response = await client.PostAsync("_api/web/lists/GetByTitle('Report')/items", content);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
}
else
{
}

The Json for lookups is a bit tricky.  You have to specify the ID of the item in the other list or of the user if its a people picker.  Also the column name here has ‘Id’ appended onto the end of it.  If the column already ends in Id then you will end up with Id twice.  In a multivalue scenario the Json looks like this:

‘PhotosId’: { ‘results’: [2,3] }

Singe Value:

‘PhotosId’: 2

Also you need to figure out the type represented here:

‘SP.Data.ReportListItem’

This is broken up as SP . Data . YourListName ListItem.  I have not tested this with Lists that have a space in their titles so I would test that or avoid spaces.

Updating a list item by ID

client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "MERGE");
client.DefaultRequestHeaders.Add("IF-MATCH", "*");
HttpContent strContent = new StringContent(String.Concat("{ '__metadata': { 'type': 'SP.List' }, 'Title': '", filename, "' }"));
strContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
strContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
HttpResponseMessage updateResponse = await client.PostAsync(String.Concat("_api/web/lists/GetByTitle('Project Photos')/Items(", id, ")"), strContent);
updateResponse.EnsureSuccessStatusCode();
if (updateResponse.IsSuccessStatusCode)
{}

Hopefully everyone finds this article helpful.  I do not wish to spend the rest of my days writing JavaScript every time I need to work on applications for mobility and I assume that at least a few other developers share my sentiments.