Sunday, August 24, 2014

Using the HttpClient Class with SharePoint 2013 REST API

Technorati Tags: ,

The System.Net.Http.HttpClient class is new in .Net framework 4.5 and was introduced under the ASP.Net Web API. The class and has many methods that support asynchronous programming and  is the best choice for writing client apps that make HTTP requests. Compared to the traditional System.Net.HttpWebRequest class not only does the HttpClient class have more options it also has extension methods such as PostAsJsonAsync and PostAsXmlAsync  methods which are available in System.Net.Http.Formatting assembly shipped with ASP.Net MVC 4 framework. In this post I am going to give you a tip on how to successfully post to the SharePoint REST API using the HttpClient class. There are many examples on how to post to the SharePoint REST API using the HttpWebRequest class but hardly any using the HttpClient class. If you are not careful you would think that the HttpClient class did not work with the SharePoint REST API.

I Keep Getting a 400 Bad Request Message when Posting

Below is a small example on how to use the HttpClient to create a folder in SharePoint 2013. The key to success is setting the Content-Type header correctly when posting. If you have used the REST API you know you must set the Content-Type header to “application/json; odata=verbose”. If you don’t you will get a “400 Bad Request” error. You can use the HttpClient.DefaultRequestHeaders collection to add headers but when trying to add the “Content-Type” header the collection will throw an “InvalidOperationException” with this message “{"Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."}”. So ok I must not be setting the content-type correctly on the HttpContent object. The StringContent class is what you are supposed to use as an argument when calling the HttpClient.PostAsync method. Looking at the StringContent class your first inclination is to use the constructor and give it the json that you want to post. The constructor takes the json, encoding type and media type as arguments. The media type corresponds to the content-type.

StringContent strContent = new StringContent(json, System.Text.Encoding.UTF8, "application/json;odata=verbose");

Unfortunately sending “application/json;odata=verbose” as the media type argument causes a “FormatException” with the message {"The format of value 'application/json;odata=verbose' is invalid."}. If you just use “application/json” you will receive a “400 bad request” error because the “odata=verbose” is missing.  So how do you get around this. First of all you must create the StringContent object with the json as the only argument to the constructor and then set the StringContent.Headers.ContentType property to “application/json;odata=verbose” using the MediaTypeHeaderValue.Parse method.

StringContent strContent = new StringContent(json);               
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");

Mystery solved.

private void CreateFolder(HttpClient client, string digest)
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");

string json = "{'__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': '/shared documents/folderhttp1'}";
client.DefaultRequestHeaders.Add("ContentLength", json.Length.ToString());
try
{
StringContent strContent = new StringContent(json);
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
HttpResponseMessage response = client.PostAsync("_api/web/folders", strContent).Result;

response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync();
}
else
{
var content = response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}

}

HttpClient is Here


HttpClient is a modern HTTP client for .NET. It provides a flexible and extensible API for accessing all things exposed through HTTP. You should use it instead of the HttpWebRequest. You can read more about it here System.Net.Http. Another great source of information when using the HttpClient with SharePoint REST is Dennis RedField’s blog Cloud 2013 or Bust. This blog has an in depth 4 part series on how to use the HttpClient with SharePoint REST. Changing our habits as developers can be a slow process. However, some new APIs can be confusing especially when used against SharePoint 2013. SharePoint 2013 is not fully OData compliant yet and has some quirks, namely content-type checking.  I hope this tip can save you some time.

4 comments:

Ganesan said...

Really a very good blog.Thanks steve

Tarjei Næss Skrede said...

Great post and a large thanks.
This post saved my day after several hours of banging my head against the SharePoint 2013 REST API wall.

Christian Lyck said...

Hi
Perhaps you or one of the others in the thread have time to help me? I still get 400 Bad Request. I can not understand why it should be so difficult to make these Rest call it's super easy from a Web Browser

public async Task GetItems()
{
_token = GetDigets();
string digest = _token;
string tempVal = "Error ";
try
{
string url = apiBaseUri;
HttpClient client = new HttpClient(new HttpClientHandler()
{
Credentials = new NetworkCredential(user, password, domain)
});
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
// client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");

client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");

string json = "{'__metadata': { 'type': 'SP.List' }, 'ServerRelativeUrl': '/getbytitle('Pharmakonlist')/Items'}";
client.DefaultRequestHeaders.Add("ContentLength", json.Length.ToString());

StringContent strContent = new StringContent(json);
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
HttpResponseMessage response = new HttpResponseMessage();

//response = await client.PostAsync("_api/web/lists/getbytitle('Pharmakonlist')/GetItems(query=@v1)?@v1={'ViewXml':''}", strContent);
response = client.PostAsync("_api/web/lists", strContent).Result; //This don´t work - 400 Bad Request

response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync();
string test = content.Result.ToString(); // This is XML

// var d = JObject.Parse(content.Result);
//JObject results = d.SelectTokens("d");
// string content = response.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{ string Tempmessage = ex.Message; }
outPut = tempVal;
// return tempVal;
}

Anonymous said...

Thanks a lot for your post, was banging my head against the wall for a bit with the "misused header name". Most of the C# code samples out there are CSOM and the REST ones are JS... C# + REST samples are harder to come by.

- Keith (WA, Australia)

Post a Comment