Overview
Since the introduction of REST interface in SharePoint 2010 you have probably used WebClient or HttpWebRequest in .NET applications.
HttpClient is a modern HTTP client for .NET, it provides a flexible and extensible API for accessing resources via HTTP(S).
HttpClient offers some advantages over WebClient/HttpWebRequest such as:
- An HttpClient instance is the place to configure extensions, set default headers, cancel outstanding requests and more.
- You can issue as many requests as you like through a single HttpClient instance.
- HttpClients are not tied to particular HTTP server or host; you can submit any HTTP request using the same HttpClient instance.
- You can derive from HttpClient to create specialized clients for particular sites or patterns
- HttpClient uses the new Task-oriented pattern for handling asynchronous requests making it dramatically easier to manage and coordinate multiple outstanding requests.
HttpClient for SharePoint Online
The solution consists of:
- SPHttpClient class that inherits from HttpClient and provides some additional SharePoint specific functionaly such as getting request digest
- SPHttpClientHandler class that basically hides all the intricacies related to SharePoint Online authentication
SharePoint Online client implementation
using System; | |
using System.Collections.Generic; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Linq; | |
namespace SharePoint.Client | |
{ | |
/// <summary> | |
/// Http client for SharePoint Online | |
/// </summary> | |
public class SPHttpClient : HttpClient | |
{ | |
public SPHttpClient(Uri webUri, string userName, string password) : base(new SPHttpClientHandler(webUri, userName, password)) | |
{ | |
BaseAddress = webUri; | |
} | |
/// <summary> | |
/// Execure request method | |
/// </summary> | |
/// <param name="requestUri"></param> | |
/// <param name="method"></param> | |
/// <param name="headers"></param> | |
/// <param name="payload"></param> | |
/// <returns></returns> | |
public JObject ExecuteJson<T>(string requestUri, HttpMethod method, IDictionary<string, string> headers, T payload) | |
{ | |
HttpResponseMessage response; | |
switch (method.Method) | |
{ | |
case "POST": | |
var requestContent = new StringContent(JsonConvert.SerializeObject(payload)); | |
requestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose"); | |
DefaultRequestHeaders.Add("X-RequestDigest", RequestFormDigest()); | |
if (headers != null) | |
{ | |
foreach (var header in headers) | |
{ | |
DefaultRequestHeaders.Add(header.Key, header.Value); | |
} | |
} | |
response = PostAsync(requestUri, requestContent).Result; | |
break; | |
case "GET": | |
response = GetAsync(requestUri).Result; | |
break; | |
default: | |
throw new NotSupportedException(string.Format("Method {0} is not supported", method.Method)); | |
} | |
response.EnsureSuccessStatusCode(); | |
var responseContent = response.Content.ReadAsStringAsync().Result; | |
return String.IsNullOrEmpty(responseContent) ? new JObject() : JObject.Parse(responseContent); | |
} | |
public JObject ExecuteJson<T>(string requestUri, HttpMethod method, T payload) | |
{ | |
return ExecuteJson(requestUri, method, null, payload); | |
} | |
public JObject ExecuteJson(string requestUri) | |
{ | |
return ExecuteJson(requestUri, HttpMethod.Get, null, default(string)); | |
} | |
/// <summary> | |
/// Request Form Digest | |
/// </summary> | |
/// <returns></returns> | |
public string RequestFormDigest() | |
{ | |
var endpointUrl = string.Format("{0}/_api/contextinfo", BaseAddress); | |
var result = this.PostAsync(endpointUrl, new StringContent(string.Empty)).Result; | |
result.EnsureSuccessStatusCode(); | |
var content = result.Content.ReadAsStringAsync().Result; | |
var contentJson = JObject.Parse(content); | |
return contentJson["d"]["GetContextWebInformation"]["FormDigestValue"].ToString(); | |
} | |
} | |
} |
SharePoint Online HTTP handler implementation
using System; | |
using System.Net; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using System.Security; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Microsoft.SharePoint.Client; | |
namespace SharePoint.Client | |
{ | |
public class SPHttpClientHandler : HttpClientHandler | |
{ | |
public SPHttpClientHandler(Uri webUri, string userName, string password) | |
{ | |
CookieContainer = GetAuthCookies(webUri, userName, password); | |
FormatType = FormatType.JsonVerbose; | |
} | |
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |
{ | |
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f"); | |
if (FormatType == FormatType.JsonVerbose) | |
{ | |
//request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json;odata=verbose")); | |
request.Headers.Add("Accept", "application/json;odata=verbose"); | |
} | |
return base.SendAsync(request, cancellationToken); | |
} | |
/// <summary> | |
/// Retrieve SPO Auth Cookies | |
/// </summary> | |
/// <param name="webUri"></param> | |
/// <param name="userName"></param> | |
/// <param name="password"></param> | |
/// <returns></returns> | |
private static CookieContainer GetAuthCookies(Uri webUri, string userName, string password) | |
{ | |
var securePassword = new SecureString(); | |
foreach (var c in password) { securePassword.AppendChar(c); } | |
var credentials = new SharePointOnlineCredentials(userName, securePassword); | |
var authCookie = credentials.GetAuthenticationCookie(webUri); | |
var cookieContainer = new CookieContainer(); | |
cookieContainer.SetCookies(webUri, authCookie); | |
return cookieContainer; | |
} | |
public FormatType FormatType { get; set; } | |
} | |
public enum FormatType | |
{ | |
JsonVerbose, | |
Xml | |
} | |
} |
Working with list items with REST
The following example shows how to retrieve all of a list’s items:
using (var client = new SPHttpClient(webUri, userName, password)) | |
{ | |
var listTitle = "Tasks"; | |
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items",webUri,listTitle); | |
var data = client.ExecuteJson(endpointUrl); | |
foreach (var item in data["value"]) | |
{ | |
Console.WriteLine(item["Title"]); | |
} | |
} |
The following example shows how to retrieve a specific list item:
using (var client = new SPHttpClient(webUri, userName, password)) | |
{ | |
var listTitle = "Tasks"; | |
var itemId = 1; | |
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items({2})",webUri,listTitle,itemId); | |
var data = client.ExecuteJson(endpointUrl); | |
Console.WriteLine(data["Title"]); | |
} |
The following example shows how to create a list item:
using (var client = new SPHttpClient(webUri, userName, password)) | |
{ | |
var listTitle = "Tasks"; | |
var itemPayload = new { __metadata = new { type = "SP.Data.TasksListItem" }, Title = "Approval Task"}; | |
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items",webUri,listTitle); | |
var data = client.ExecuteJson(endpointUrl,HttpMethod.Post,itemPayload); | |
Console.WriteLine("Task item '{0}' has been created",data["Title"]); | |
} |
The following example shows how to update a list item:
using (var client = new SPHttpClient(webUri, userName, password)) | |
{ | |
var listTitle = "Tasks"; | |
var itemId = 1; | |
var itemPayload = new { __metadata = new { type = "SP.Data.TasksListItem" }, Title = "Approval Task"}; | |
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items({2})",webUri,listTitle,itemId); | |
var headers = new Dictionary<string,string>(); | |
headers["IF-MATCH"] = "*"; | |
headers["X-HTTP-Method"] = "MERGE"; | |
client.ExecuteJson(endpointUrl,HttpMethod.Post,headers, itemPayload); | |
Console.WriteLine("Task item has been updated"); | |
} |
The following example shows how to delete a list item:
using (var client = new SPHttpClient(webUri, userName, password)) | |
{ | |
var listTitle = "Tasks"; | |
var itemId = 2; | |
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items({2})",webUri,listTitle,itemId); | |
var headers = new Dictionary<string,string>(); | |
headers["IF-MATCH"] = "*"; | |
headers["X-HTTP-Method"] = "DELETE"; | |
client.ExecuteJson(endpointUrl,HttpMethod.Post,null,headers); | |
Console.WriteLine("Task item has been deleted"); | |
} |