Come utilizzare System.Net.HttpClient per pubblicare un tipo complesso?


102

Ho un tipo complesso personalizzato con cui voglio lavorare usando l'API Web.

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

Ed ecco il mio metodo di controller API web. Voglio pubblicare questo oggetto in questo modo:

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

E ora vorrei utilizzare System.Net.HttpClientper effettuare la chiamata al metodo. Tuttavia, non sono sicuro di quale tipo di oggetto passare nel PostAsyncmetodo e come costruirlo. Ecco alcuni esempi di codice client.

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

Come faccio a creare l' HttpContentoggetto in modo che l'API web lo comprenda?


Hai provato a inviare una versione serializzata XML del tuo oggetto all'endpoint del servizio?
Joshua Drake

Risposte:


132

Il generico HttpRequestMessage<T>è stato rimosso . Questo :

new HttpRequestMessage<Widget>(widget)

sarà non funzionano più .

Invece, da questo post , il team ASP.NET ha incluso alcune nuove chiamate per supportare questa funzionalità:

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

Quindi, il nuovo codice ( da dunston ) diventa:

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

1
Sì, ma cosa succede se non hai accesso alla classe Widget?
contattateci

13
Le nuove HttpClient.PostAsXXXAsync<T>( T value ) methods are great, but what about one for application/x-www-form-urlencoded format? Is there a simple / short way for that or do we still need to create elaborate liste KeyValuePair`?
Jaans

1
@Jaans Flurl.Http fornisce un modo semplice / breve tramite PostUrlEncodedAsync.
Todd Menier

16
Si noti che è necessario aggiungere un riferimento a System.Net.Http.Formatting per poter utilizzare PostAsJsonAsyncoPostAsXmlAsync
Pete

6
Per usare PostAsJsonAcync, aggiungi il pacchetto NuGet Microsoft.AspNet.WebApi.Client !!
Dennis

99

Dovresti SendAsyncinvece usare il metodo, questo è un metodo generico, che serializza l'input al servizio

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

Se non vuoi creare la classe concreta, puoi farlo con la FormUrlEncodedContentclasse

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

Nota: devi rendere il tuo id un int ( int?) nullable


1
Questo verrà chiamato da un progetto esterno, dove non avrò un riferimento all'assembly che contiene l'oggetto Widget. Ho provato a creare un oggetto digitato in modo anonimo che contiene le proprietà corrette, serializzandolo utilizzando questo metodo e passandolo in questo modo, ma ottengo un errore interno del server 500. Non colpisce mai il metodo del controller API web.
indot_brad

Oh, allora devi pubblicare xml o json nel servizio webapi e questo lo deserializzerà - fa lo stesso, SendAsync, sta serializzando l'oggetto per il servizio
Dunston

1
Ho appena fatto un aggiornamento - ho testato il codice, ma con un codice più semplice, ma dovrei funzionare
Dunston

8
Ricevo "Il tipo non generico" System.Net.Http.HttpRequestMessage "non può essere utilizzato con argomenti di tipo". è ancora valido?
user10479

5
Già la prima soluzione non funziona più: aspnetwebstack.codeplex.com/discussions/350492
Giovanni B

74

Tieni presente che se stai usando una libreria di classi portabile, HttpClient non avrà il metodo PostAsJsonAsync . Per pubblicare un contenuto come JSON usando una libreria di classi portabile, dovrai fare questo:

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

Quando argsAsJson proviene da un oggetto serializzato e questo oggetto ha una proprietà es. Content = "dominio \ utente", quindi \ verrà codificato due volte. Una volta quando serializzato in argsAsJson e la seconda volta quando PostAsync pubblica contentPost. Come evitare la doppia codifica?
Krzysztof Morcinek

3
Eccellente @fabiano! Questo ha davvero funzionato. Questi due argomenti aggiuntivi sono necessari in questo tipo di progetti.
Peter Klein

Molto bene @PeterKlein! Non sono riuscito a trovare queste informazioni nella documentazione di Microsoft sul Web, quindi questo può aiutare altri con lo stesso problema. Il mio progetto semplicemente non invia dati senza questo trucco.
Fabiano

1
Tieni presente che potresti anche dover aggiungere "application / json" nell'intestazione Accept della richiesta, per stackoverflow.com/a/40375351/3063273
Matt Thomas

4

Se vuoi i tipi di metodi di convenienza menzionati in altre risposte ma hai bisogno della portabilità (o anche se non lo fai), potresti voler controllare Flurl [divulgazione: sono l'autore]. Avvolge (sottilmente) HttpCliente Json.NET e aggiunge un po 'di zucchero fluente e altre prelibatezze, inclusi alcuni aiutanti di test integrati .

Pubblica come JSON:

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

o con codifica URL:

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

Entrambi gli esempi sopra restituiscono un HttpResponseMessage, ma Flurl include metodi di estensione per restituire altre cose se vuoi solo andare al sodo:

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

Flurl è disponibile su NuGet:

PM> Install-Package Flurl.Http

1

Dopo aver esaminato molte alternative, mi sono imbattuto in un altro approccio, adatto alla versione API 2.0.

(VB.NET è il mio preferito, quindi ...)

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

In bocca al lupo! Per me questo ha funzionato (alla fine!).

Saluti, Peter


1
Questo CON i suggerimenti dati sopra da @Fabiano fa accadere le cose.
Peter Klein

2
VB.NET non è il preferito di nessuno :)
Lazy Coder

1

Penso che tu possa farlo:

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

1

Nel caso in cui qualcuno come me non avesse davvero capito di cosa si parla sopra, do un semplice esempio che funziona per me. Se si dispone di un'API Web il cui URL è " http://somesite.com/verifyAddress ", è un metodo di pubblicazione e richiede che gli venga passato un oggetto indirizzo. Vuoi chiamare questa API nel tuo codice. Ecco cosa puoi fare.

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

0

Questo è il codice con cui sono finito, basato sulle altre risposte qui. Questo è per un HttpPost che riceve e risponde con tipi complessi:

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

-1

Effettua una chiamata di servizio come questa:

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

E il metodo di servizio come questo:

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync si occupa della serializzazione e deserializzazione sulla rete


Questo invierà un messaggio HTTP PUT, non un POST come richiesto. Come altri hanno già detto PostAsJsonAsyncinvierà i dati richiesti, come POST in JSON.
Zhaph - Ben Duguid
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.