How to use System.Net.HttpClient to post a complex type?

C#asp.net Web-Api

C# Problem Overview


I have a custom complex type that I want to work with using Web API.

public class Widget
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

And here is my web API controller method. I want to post this object like so:

public class TestController : ApiController
{
    // POST /api/test
    public HttpResponseMessage<Widget> Post(Widget widget)
    {
        widget.ID = 1; // hardcoded for now. TODO: Save to db and return newly created ID

        var response = new HttpResponseMessage<Widget>(widget, HttpStatusCode.Created);
        response.Headers.Location = new Uri(Request.RequestUri, "/api/test/" + widget.ID.ToString());
        return response;
    }
}

And now I'd like to use System.Net.HttpClient to make the call to the method. However, I'm unsure of what type of object to pass into the PostAsync method, and how to construct it. Here is some sample client code.

var client = new HttpClient();
HttpContent content = new StringContent("???"); // how do I construct the Widget to post?
client.PostAsync("http://localhost:44268/api/test", content).ContinueWith(
    (postTask) =>
    {
        postTask.Result.EnsureSuccessStatusCode();
    });

How do I create the HttpContent object in a way that web API will understand it?

C# Solutions


Solution 1 - C#

The generic HttpRequestMessage<T> has been removed. This :

new HttpRequestMessage<Widget>(widget)

will no longer work.

Instead, from this post, the ASP.NET team has included some new calls to support this functionality:

HttpClient.PostAsJsonAsync<T>(T value) sends “application/json”
HttpClient.PostAsXmlAsync<T>(T value) sends “application/xml”

So, the new code (from dunston) becomes:

Widget widget = new Widget()
widget.Name = "test"
widget.Price = 1;

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:44268");
client.PostAsJsonAsync("api/test", widget)
    .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode() );

Solution 2 - C#

You should use the SendAsync method instead, this is a generic method, that serializes the input to the service

Widget widget = new Widget()
widget.Name = "test"
widget.Price = 1;

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:44268/api/test");
client.SendAsync(new HttpRequestMessage<Widget>(widget))
    .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode() );

If you don't want to create the concrete class, you can make it with the FormUrlEncodedContent class

var client = new HttpClient();

// This is the postdata
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("Name", "test"));
postData.Add(new KeyValuePair<string, string>("Price ", "100"));

HttpContent content = new FormUrlEncodedContent(postData); 

client.PostAsync("http://localhost:44268/api/test", content).ContinueWith(
    (postTask) =>
    {
        postTask.Result.EnsureSuccessStatusCode();
    });

Note: you need to make your id to a nullable int (int?)

Solution 3 - C#

Note that if you are using a Portable Class Library, HttpClient will not have PostAsJsonAsync method. To post a content as JSON using a Portable Class Library, you will have to do this:

HttpClient client = new HttpClient();
HttpContent contentPost = new StringContent(argsAsJson, Encoding.UTF8, 
"application/json");

await client.PostAsync(new Uri(wsUrl), contentPost).ContinueWith(
(postTask) => postTask.Result.EnsureSuccessStatusCode());

Solution 4 - C#

If you want the types of convenience methods mentioned in other answers but need portability (or even if you don't), you might want to check out Flurl [disclosure: I'm the author]. It (thinly) wraps HttpClient and Json.NET and adds some fluent sugar and other goodies, including some baked-in testing helpers.

Post as JSON:

var resp = await "http://localhost:44268/api/test".PostJsonAsync(widget);

or URL-encoded:

var resp = await "http://localhost:44268/api/test".PostUrlEncodedAsync(widget);

Both examples above return an HttpResponseMessage, but Flurl includes extension methods for returning other things if you just want to cut to the chase:

T poco = await url.PostJsonAsync(data).ReceiveJson<T>();
dynamic d = await url.PostUrlEncodedAsync(data).ReceiveJson();
string s = await url.PostUrlEncodedAsync(data).ReceiveString();

Flurl is available on NuGet:

PM> Install-Package Flurl.Http

Solution 5 - C#

After investigating lots of alternatives, I have come across another approach, suitable for the API 2.0 version.

(VB.NET is my favorite, sooo...)

Public Async Function APIPut_Response(ID as Integer, MyWidget as Widget) as Task(Of HttpResponseMessage)
    Dim DesiredContent as HttpContent = New StringContent(JsonConvert.SerializeObject(MyWidget))
    Return Await APIClient.PutAsync(String.Format("api/widget/{0}", ID), DesiredContent)
End Function

Good luck! For me this worked out (in the end!).

Regards, Peter

Solution 6 - C#

I think you can do this:

var client = new HttpClient();
HttpContent content = new Widget();
client.PostAsync<Widget>("http://localhost:44268/api/test", content, new FormUrlEncodedMediaTypeFormatter())
    .ContinueWith((postTask) => { postTask.Result.EnsureSuccessStatusCode(); });

Solution 7 - C#

In case someone like me didn't really understand what all above are talking about, I give an easy example which is working for me. If you have a web api which url is "http://somesite.com/verifyAddress";, it is a post method and it need you to pass it an address object. You want to call this api in your code. Here what you can do.

    public Address verifyAddress(Address address)
    {
        this.client = new HttpClient();
        client.BaseAddress = new Uri("http://somesite.com/");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var urlParm = URL + "verifyAddress";
        response = client.PostAsJsonAsync(urlParm,address).Result;
        var dataObjects = response.IsSuccessStatusCode ? response.Content.ReadAsAsync<Address>().Result : null;
        return dataObjects;
    }

Solution 8 - C#

This is the code I wound up with, based upon the other answers here. This is for an HttpPost that receives and responds with complex types:

Task<HttpResponseMessage> response = httpClient.PostAsJsonAsync(
                       strMyHttpPostURL,
                       new MyComplexObject { Param1 = param1, Param2 = param2}).ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
                    //debug:
                    //String s = response.Result.Content.ReadAsStringAsync().Result;
                    MyOtherComplexType moct = (MyOtherComplexType)JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result, typeof(MyOtherComplexType));

Solution 9 - C#

Make a service call like this:

public async void SaveActivationCode(ActivationCodes objAC)
{
    var client = new HttpClient();
    client.BaseAddress = new Uri(baseAddress);
    HttpResponseMessage response = await client.PutAsJsonAsync(serviceAddress + "/SaveActivationCode" + "?apiKey=445-65-1216", objAC);
} 

And Service method like this:

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync takes care of Serialization and deserialization over the network

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionindot_bradView Question on Stackoverflow
Solution 1 - C#Joshua BallView Answer on Stackoverflow
Solution 2 - C#dunstonView Answer on Stackoverflow
Solution 3 - C#FabianoView Answer on Stackoverflow
Solution 4 - C#Todd MenierView Answer on Stackoverflow
Solution 5 - C#user2366741View Answer on Stackoverflow
Solution 6 - C#Marius StănescuView Answer on Stackoverflow
Solution 7 - C#user3293338View Answer on Stackoverflow
Solution 8 - C#LodlaidenView Answer on Stackoverflow
Solution 9 - C#Nitin NayyarView Answer on Stackoverflow