How to use HttpClient to post JSON data
This is a common request in forums so I will show you how to use the new HttpClient class and the DataContractJsonSerializer to post JSON data to a web service. Posting serialized JSON objects to web endpoints is a common way to get data over HTTP and HTTPS to and end point and although this is extremely easy in the WinJS realm (when writing HTML5/JavaScript Windows Store apps), there is not a good example of how to do this using HttpClient for the .NET applications.
Step 1 – Find out what data you need to send
If you are sending JSON data to a WebService the service itself should have an example of what it expects. Often there is sample code that you can use to generate a POST. Start by capturing a successful POST to the web service and capture the HTTP or HTTPS traffic. This will allow you to compare your POST to something you know is correct. One tool that will help you immensely while developing apps using HTTP and HTTPS protocols is Fiddler. In my sample I captured the HTTP traffic to a Windows Azure Mobile Services endpoint.
Here is the HTTP traffic I captured with Fiddler from the sample application generated by Mobile Services when I POST data to the background database.
POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1
Accept: application/json
X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd
X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20
Content-Type: application/json
Host: sharemydata.azure-mobile.net
Content-Length: 31
Expect: 100-continue
{"text":"ddd","complete":false}
Instead of using the magic of the MobileServices classes provided, In the next step I will show how to use the new HttpClient classes to show how to POST the same data I captured above. You should then be able to extend this to your case when you need to POST using JSON. Again the intent is not to bypass the libraries we have provided for you, simply show how one JSON based web service can be analyzed and you can use this to troubleshoot and POST to it using the HttpClient and associated classes.
Step 2 – Understand what is being POSTED and duplicate it
Let’s walk through the trace I took above line by line and create code to duplicate this:
POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1
// I will create an HttpClient and POST to the Uri https://sharemydata.azure-mobile.net/tables/TodoItem
// Create HttpClient instance to use for the app
HttpClient aClient = new HttpClient();
// Uri is where we are posting to:
Uri theUri = new Uri("https://sharemydata.azure-mobile.net/tables/TodoItem");
// use the Http client to POST some content ( ‘theContent’ not yet defined).
aClient.PostAsync(theUri, theContent);
Accept: application/json
// I have to let the server know I accept json data when it replies so this will add that header
aClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd
I saw from the webservice documentation this is optional so I could leave it out (but I won’t).
aClient.DefaultRequestHeaders.Add("X-ZUMO-INSTALLATION-ID", "8bc6aea9-864a-44fc-9b4b-87ec64e123bd");
X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20
Set this header to let the service know what application is trying to insert data
aClient.DefaultRequestHeaders.Add("X-ZUMO-APPLICATION","OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20");
Host: sharemydata.azure-mobile.net
aClient.DefaultRequestHeaders.Host = theUri.Host;
Expect: 100-continue
This is added by default so you do not have to do anything
The Content headers and information
You will notice I skipped Content-Type: application/json, Content-Length: 31 and {"text":"ddd","complete":false}.
All this is content related will be added by assigning ‘theContent’ as a StringContent object in the PostAsync call above. You can certainly send simple text and format it yourself to be a JSON string and this is fine for simple JSON posts. If you have a complicated object it is better to let our classes do the work for you!
- Define a class that reflects the JSON data posted.
[DataContract]
public class TodoItem2
{
public int Id { get; set; }[DataMember(Name = "text")]
public string Text { get; set; }[DataMember(Name = "complete")]
public bool Complete { get; set; }
}Here I defined a class to represent the JSON data I will post. The attributes ‘DataContract’ and ‘DataMember’ are necessary for the JSON serialization class. You will see that ‘DataMember’ further defines the name ‘text’ and ‘complete’ which are lower case representations of the public members of the class. This illustrates that you can adorn and existing class and change the Name of the DataMember used to generate the serialized JSON data. Also notice that the public int ‘Id’ does not have a DataMember attribute. This will hide this attribute from the serialization.
- Create and Populate the class with data
You simply instantiate and assign the data to the is class:
//Class that will be serialized into Json and posted
TodoItem2 todoItem2 = new TodoItem2();
//Set some values
todoItem2.Text = "ddd";
todoItem2.Complete = false;
- Add code to serialize the object to a string and create a StringContent object to pass to the PostAsync call
//Create a Json Serializer for our type
DataContractJsonSerializer jsonSer = new DataContractJsonSerializer(typeof(TodoItem2));// use the serializer to write the object to a MemoryStream
MemoryStream ms = new MemoryStream();
jsonSer.WriteObject( ms, todoItem2);
ms.Position = 0;//use a Stream reader to construct the StringContent (Json)
StreamReader sr = new StreamReader(ms);
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8,"application/json");
Step 3 – Validate your POST looks correct and works
This is all you need to do in order to stream the content of the object to the web service. Now when I run this code in my app and take a trace with Fiddler I see that the POST succeeded with a 201 and the POST indeed is practically identical to my previous successful case (the order of the headers is different and there is a charset attached to the Content-Type which is fine):
POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1
Accept: application/json
X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd
X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20
Content-Type: application/json; charset=utf-8
Host: sharemydata.azure-mobile.net
Content-Length: 31
Expect: 100-continue
Connection: Keep-Alive
{"complete":false,"text":"ddd"}
Summary
If you realize that POSTing JSON data is simply providing the correct HTTP or HTTPS traffic to an endpoint, you can use the above code to build your own code that will communicate with whatever endpoint you want. You are really just sending a string with the appropriate headers. Receiving JSON data is similar and will be covered in a following POST if there is enough interest (however it is fairly well documented).
Follow us on Twitter @wsdevsol!
Note: I changed the data in this post to a fictional end point. You will need to create your own Windows Azure Mobile Services application to try this yourself or identify your own endpoint that accepts JSON data!
Completed Code:
C#
[DataContract]
public class TodoItem2
{
public int Id { get; set; }
[DataMember(Name = "text")]
public string Text { get; set; }
[DataMember(Name = "complete")]
public bool Complete { get; set; }
}
//Where we are posting to:
Uri theUri = new Uri("https://sharemydata.azure-mobile.net/tables/TodoItem");
//Create an Http client and set the headers we want
HttpClient aClient = new HttpClient();
aClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
aClient.DefaultRequestHeaders.Add("X-ZUMO-INSTALLATION-ID", "8bc6aea9-864a-44fc-9b4b-87ec64e123bd");
aClient.DefaultRequestHeaders.Add("X-ZUMO-APPLICATION", "OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20");
aClient.DefaultRequestHeaders.Host = theUri.Host;
//Class that will be serialized into Json and posted
TodoItem2 todoItem2 = new TodoItem2();
//Set some values
todoItem2.Text = "ddd";
todoItem2.Complete = false;
//Create a Json Serializer for our type
DataContractJsonSerializer jsonSer = new DataContractJsonSerializer(typeof(TodoItem2));
// use the serializer to write the object to a MemoryStream
MemoryStream ms = new MemoryStream();
jsonSer.WriteObject(ms, todoItem2);
ms.Position = 0;
//use a Stream reader to construct the StringContent (Json)
StreamReader sr = new StreamReader(ms);
// Note if the JSON is simple enough you could ignore the 5 lines above that do the serialization and construct it yourself
// then pass it as the first argument to the StringContent constructor
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, "application/json");
//Post the data
HttpResponseMessage aResponse = await aClient.PostAsync(theUri, theContent);
if (aResponse.IsSuccessStatusCode)
{
}
else
{
// show the response status code
String failureMsg = "HTTP Status: " + aResponse.StatusCode.ToString() + " - Reason: " + aResponse.ReasonPhrase;
}
VB
Public Class TodoItem2
Private Property Id() As Integer
Get
Return m_Id
End Get
Set(value As Integer)
m_Id = value
End Set
End Property
Private m_Id As Integer
Public Property Text() As String
Get
Return m_Text
End Get
Set(value As String)
m_Text = value
End Set
End Property
Private m_Text As String
Public Property Complete() As Boolean
Get
Return m_Complete
End Get
Set(value As Boolean)
m_Complete = value
End Set
End Property
Private m_Complete As Boolean
End Class
Private Async Sub SendJsonPost()
'Where we are posting to:
Dim theUri As New Uri("https://sharemydata.azure-mobile.net/tables/TodoItem")
'Create an Http client and set the headers we want
Dim aClient As New HttpClient()
aClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
aClient.DefaultRequestHeaders.Add("X-ZUMO-INSTALLATION-ID", "8bc6aea9-864a-44fc-9b4b-87ec64e123bd")
aClient.DefaultRequestHeaders.Add("X-ZUMO-APPLICATION", "OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20")
aClient.DefaultRequestHeaders.Host = theUri.Host
'Class that will be serialized into Json and posted
Dim todoItem2 As New TodoItem2()
'Set some values
todoItem2.Text = "ddd"
todoItem2.Complete = False
'Create a Json Serializer for our type
Dim jsonSer As New DataContractJsonSerializer(GetType(TodoItem2))
' use the serializer to write the object to a MemoryStream
Dim ms As New MemoryStream()
jsonSer.WriteObject(ms, todoItem2)
ms.Position = 0
Dim sr As New StreamReader(ms)
'Note if the JSON is simple enough you could ignore the 5 lines above that do the serialization and construct it yourself
'then pass it as the first argument to the StringContent constructor
Dim theContent As New StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, "application/json")
'Post the data
Dim aResponse As HttpResponseMessage = Await aClient.PostAsync(theUri, theContent)
If (aResponse.IsSuccessStatusCode) Then
Else
'show the response status code
Dim failureMsg = "HTTP Status: " + aResponse.StatusCode.ToString() + " - Reason: " + aResponse.ReasonPhrase
End If
End Sub
Comments
Anonymous
May 21, 2013
Hi, Does this code work with Visual Studio 2012 with Windows phone 8 sdk. (I have got the HttpWebClient)Anonymous
May 23, 2013
You don't need to set the HOST header manually, and allegedly doing so prevents the request from going to a proxy server properly. stackoverflow.com/.../capturing-windows-store-app-traffic-with-fiddler-raises-an-exceptionAnonymous
January 13, 2014
Great article! this is exactly what I was looking for! Thanks a lotAnonymous
February 18, 2014
The old customers webserivice worked with application/x-www-form-urlencoded . As they changed the service this helps me a lot to rebuid it to jsonAnonymous
July 08, 2015
Thank you for this article. It's really helpful for meAnonymous
November 15, 2015
What bad blog format! may be its useful but it is not readable and really terrible.