Jak przekazać obiekt do HttpClient.PostAsync i serializować jako treść JSON?

94

Używam System.Net.Http, znalazłem kilka przykładów w sieci. Udało mi się stworzyć ten kod do złożenia POSTwniosku:

public static string POST(string resource, string token)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(baseUri);
        client.DefaultRequestHeaders.Add("token", token);

        var content = new FormUrlEncodedContent(new[]
        {
             new KeyValuePair<string, string>("", "")
        });

        var result = client.PostAsync("", content).Result;
        string resultContent = result.Content.ReadAsStringAsync().Result;
        return resultContent;
    }
 }

wszystko działa dobrze. Ale przypuśćmy, że chcę przekazać POSTmetodzie trzeci parametr , tzw data. Param danych to taki obiekt:

object data = new
{
    name = "Foo",
    category = "article"
};

jak mogę to zrobić bez tworzenia KeyValuePair? Mój php RestAPIczeka na wejście json, więc FormUrlEncodedContentpowinien rawpoprawnie wysłać json. Ale jak mam to zrobić Microsoft.Net.Http? Dzięki.

IlDrugo
źródło
Jeśli rozumiem twoje pytanie, chcesz wysyłać zawartość JSON zamiast treści zakodowanej w formularzu w prawo (a przez rozszerzenie chcesz, aby twój anonimowy typ był serializowany jako JSON do tej treści)?
CodingGorilla
@CodingGorilla yes to typ anonimowy.
IlDrugo,
3
Na marginesie dla przyszłych czytelników, nie należy używać usingdla HttpClient. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
2
Uwaga od firmy Microsoft, dlaczego usingnie należy jej używać: HttpClient is intended to be instantiated once and reused throughout the life of an application. The following conditions can result in SocketException errors: Creating a new HttpClient instance per request. Server under heavy load. Creating a new HttpClient instance per request can exhaust the available sockets. docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/ ...
Ogglas

Odpowiedzi:

155

Prosta odpowiedź na twoje pytanie brzmi: Nie. Podpis PostAsyncmetody jest następujący:

public Task PostAsync (Uri requestUri, zawartość HttpContent)

Tak więc, chociaż możesz przekazać objectdo PostAsync, musi być typu, HttpContenta twój anonimowy typ nie spełnia tych kryteriów.

Istnieją jednak sposoby na osiągnięcie tego, co chcesz osiągnąć. Najpierw musisz serializować swój anonimowy typ do formatu JSON, najczęściej używanym narzędziem do tego jest Json.NET . A kod do tego jest dość trywialny:

var myContent = JsonConvert.SerializeObject(data);

Następnie będziesz musiał skonstruować obiekt treści, aby wysłać te dane, użyję ByteArrayContentobiektu, ale możesz użyć lub utworzyć inny typ, jeśli chcesz.

var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);

Następnie chcesz ustawić typ zawartości, aby interfejs API wiedział, że to JSON.

byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

Następnie możesz wysłać zapytanie bardzo podobne do poprzedniego przykładu z treścią formularza:

var result = client.PostAsync("", byteContent).Result

Na marginesie, wywołanie .Resultwłaściwości, tak jak tutaj, może mieć złe skutki uboczne, takie jak martwe blokowanie, więc chcesz zachować ostrożność.

CodingGorilla
źródło
Okej, to bardzo jasne. Dzięki za tę odpowiedź. Tylko pytanie: kiedy POST, PUT, DELETEwykonywany jest a , zwykle zwraca API TRUE, zadeklarowałem metodę jako string, ale kiedy to zrobię: return result;Otrzymuję:, Can't Convert HttpResponseMessage in stringczy powinienem zmienić deklarację metody? Potrzebuję odpowiedzi w postaci ciągu znaków, ponieważ będę musiał ją deserializować w innej metodzie klasy.
IlDrugo,
2
Jeśli musisz zdeserializować treść odpowiedzi, to zwrócenie ciągu w sposób, w jaki masz w swoim pytaniu (za pomocą result.Content.ReadAsStringAsync()), prawdopodobnie jest w porządku. W zależności od struktury aplikacji, może być lepiej zwrócić Contentobiekt bezpośrednio, jeśli chcesz sprawdzić nagłówki, aby określić, jaki jest typ treści (np. XML lub JSON). Ale jeśli wiesz, że zawsze zwróci JSON (lub inny format), po prostu zwróć treść odpowiedzi jako ciąg znaków.
CodingGorilla,
Przepraszam, że pytam, ale czy musisz to zrobić, jeśli dane są typu StringContent?
MyDaftQuestions,
1
@MyDaftQuestions Nie jestem do końca pewien, o co pytasz, ale możesz przekazać StringContentbezpośrednio do, PostAsyncponieważ pochodzi z HttpContent.
CodingGorilla
@CodingGorilla, że było co prosiłem. Dziękuję :)
MyDaftQuestions
67

Musisz przekazać swoje dane w treści żądania jako nieprzetworzony ciąg, a nie FormUrlEncodedContent. Jednym ze sposobów jest serializowanie go do ciągu JSON:

var json = JsonConvert.SerializeObject(data); // or JsonSerializer.Serialize if using System.Text.Json

Teraz wszystko, co musisz zrobić, to przekazać ciąg znaków do metody post.

var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+

var client = new HttpClient();
var response = await client.PostAsync(uri, stringContent);
elolos
źródło
Co to jest stringContent? W moim przypadku stringContentwartość to "\"\"". Czy to poprawna wartość?
R15
czy możliwe jest uzyskanie wyniku ciągu w vb z kodu C #? okazało się, że jest podobnie ...
gumuruh
42

Prostym rozwiązaniem jest użycie Microsoft ASP.NET Web API 2.2 Clientz NuGet .

Następnie możesz po prostu to zrobić, a serializuje obiekt do JSON i ustawia Content-Typenagłówek na application/json; charset=utf-8:

var data = new
{
    name = "Foo",
    category = "article"
};

var client = new HttpClient();
client.BaseAddress = new Uri(baseUri);
client.DefaultRequestHeaders.Add("token", token);
var response = await client.PostAsJsonAsync("", data);
trydis
źródło
2
zdecydowanie PostAsJsonAsync jest do wykorzystania
Kat Lim Ruiz
26

Jest teraz prostszy sposób z .NET Standardlub .NET Core:

var client = new HttpClient();
var response = await client.PostAsync(uri, myRequestObject, new JsonMediaTypeFormatter());

UWAGA: Aby użyć JsonMediaTypeFormatterklasy, musisz zainstalować Microsoft.AspNet.WebApi.Clientpakiet NuGet, który można zainstalować bezpośrednio lub za pośrednictwem innego, takiego jak Microsoft.AspNetCore.App.

Za pomocą tego podpisu HttpClient.PostAsyncmożesz przekazać dowolny obiekt, a JsonMediaTypeFormatterwola automatycznie zajmie się serializacją itp.

Za pomocą odpowiedzi możesz HttpContent.ReadAsAsync<T>deserializować zawartość odpowiedzi do oczekiwanego typu:

var responseObject = await response.Content.ReadAsAsync<MyResponseType>();
Ken Lyon
źródło
1
jakiej wersji .net to używa? Moja wersja nie może znaleźć pola „Formatting” w przestrzeni nazw System.Net.Http
TemporaryFix
1
@Programmatic Musisz używać .NET Standardlub .NET Core, jak wspomniałem. Może używasz .NET Framework? W moim projekcie JsonMediaTypeFormatter jest ładowany stąd: C: \ Program Files \ dotnet \ sdk \ NuGetFallbackFolder \ microsoft.aspnet.webapi.client \ 5.2.6 \ lib \ netstandard2.0 \ System.Net.Http.Formatting. dll
Ken Lyon
1
@ Programmatic Jeśli korzystasz już z jednego z tych typów projektów, może być konieczne dodanie dodatkowego pakietu NuGet. Zapominam dokładnie, które zostały automatycznie uwzględnione. W moim przypadku został dołączony jako część pakietu NuGet Microsoft.AspNetCore.App.
Ken Lyon
1
Korzystałem z platformy .NET Core, ale nie sądzę, że moje rozwiązanie było ustawione na korzystanie z najnowszej wersji języka C #. Zaktualizowałem i zadziałało. Dziękuję
TemporaryFix
1
@Programmatic Nie ma za co. Miło mi słyszeć, że to działa! Dodałem notatkę do mojej odpowiedzi na temat pakietu NuGet.
Ken Lyon