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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} |
Pingback: SharePoint 2013 & Office 365: Recopilatorio de enlaces interesantes (LXV)! - Blog de Juan Carlos González en Geeks.MS
Pingback: Getting the list item Permission using Rest Api | DL-UAT
Really Helpful, thank you
Pingback: Sharepoint 2013 – Site Title
The delete api call gives a 400 Bad Request. Can you please help me diagnose what the problem really is. Thanks
Because it should be:
client.ExecuteJson(endpointUrl, HttpMethod.Post, headers, default(string));
Delete operation needs headers but does not require any payload.
Other than this – I love this post. Great work!
How we can do bulk update or insert? Any Idea would be of great help!!!
How to do bulk operation of update and insert?
You wrote:
“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.”
But in your exaples you create a HttpClient and dispose it after the call. How do you change credentials or cookies, if you want to call another host with the same HttpClient?