¿Cómo usar System.Net.HttpClient para publicar un tipo complejo?

Tengo un tipo complejo personalizado con el que quiero trabajar usando la API web.

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

Y aquí está mi método de controlador web API. Quiero publicar este objeto así:

 public class TestController : ApiController { // POST /api/test public HttpResponseMessage Post(Widget widget) { widget.ID = 1; // hardcoded for now. TODO: Save to db and return newly created ID var response = new HttpResponseMessage(widget, HttpStatusCode.Created); response.Headers.Location = new Uri(Request.RequestUri, "/api/test/" + widget.ID.ToString()); return response; } } 

Y ahora me gustaría usar System.Net.HttpClient para hacer la llamada al método. Sin embargo, no estoy seguro de qué tipo de objeto pasar al método PostAsync y cómo construirlo. Aquí hay algunos ejemplos de código de cliente.

 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(); }); 

¿Cómo creo el objeto HttpContent de forma que la API web lo entienda?

El genérico HttpRequestMessage ha sido eliminado . Esta :

 new HttpRequestMessage(widget) 

ya no funcionará

En cambio, desde esta publicación , el equipo de ASP.NET ha incluido algunas llamadas nuevas para admitir esta funcionalidad:

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

Entonces, el nuevo código ( de dunston ) se convierte en:

 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() ); 

En su lugar, debe usar el método SendAsync , este es un método genérico que serializa la entrada al servicio

 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)) .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode() ); 

Si no desea crear la clase concreta, puede hacerlo con la clase FormUrlEncodedContent

 var client = new HttpClient(); // This is the postdata var postData = new List>(); postData.Add(new KeyValuePair("Name", "test")); postData.Add(new KeyValuePair("Price ", "100")); HttpContent content = new FormUrlEncodedContent(postData); client.PostAsync("http://localhost:44268/api/test", content).ContinueWith( (postTask) => { postTask.Result.EnsureSuccessStatusCode(); }); 

Nota: debe hacer que su ID sea nullable int ( int? )

Tenga en cuenta que si está utilizando una Biblioteca de clases portátil, HttpClient no tendrá el método PostAsJsonAsync . Para publicar un contenido como JSON utilizando una Biblioteca de clases portátil, tendrá que hacer esto:

 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()); 

Si desea los tipos de métodos de conveniencia mencionados en otras respuestas pero que necesitan portabilidad (o incluso si no lo hace), es posible que desee verificar Flurl [revelación: soy el autor]. Es (finamente) envuelve HttpClient y Json.NET y agrega un poco de azúcar fluida y otras golosinas, incluyendo algunos ayudantes de prueba horneados.

Publicar como JSON:

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

o URL-codificado:

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

Los dos ejemplos anteriores devuelven un HttpResponseMessage , pero Flurl incluye métodos de extensión para devolver otras cosas si solo quieres ir al grano:

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

Flurl está disponible en NuGet:

 PM> Install-Package Flurl.Http 

Después de investigar muchas alternativas, me encontré con otro enfoque, adecuado para la versión API 2.0.

(VB.NET es mi favorito, tan …)

 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 

¡Buena suerte! Para mí esto funcionó (¡al final!).

Saludos, Peter

Creo que puedes hacer esto:

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

Este es el código con el que terminé, basado en las otras respuestas aquí. Esto es para un HttpPost que recibe y responde con tipos complejos:

 Task 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)); 

En caso de que alguien como yo no entendiera realmente de lo que todos los anteriores están hablando, doy un ejemplo fácil que funciona para mí. Si tiene una API web cuyo URL es ” http://somesite.com/verifyAddress “, es un método de publicación y necesita que le pase un objeto de dirección. Desea llamar a esta API en su código. Aquí lo que puedes hacer

  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
().Result : null; return dataObjects; }

Haga una llamada de servicio como esta:

 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); } 

Y un método de servicio como este:

 public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC) { } 

PutAsJsonAsync se encarga de la serialización y deserialización a través de la red