Поделиться через


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-exception

  • Anonymous
    January 13, 2014
    Great article! this is exactly what I was looking for! Thanks a lot

  • Anonymous
    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 json

  • Anonymous
    July 08, 2015
    Thank you for this article. It's really helpful for me

  • Anonymous
    November 15, 2015
    What bad blog format! may be its useful but it is not readable and really terrible.