Wie übergebe ich ein Objekt an HttpClient.PostAsync und serialisiere es als JSON-Body?

89

Ich benutze System.Net.Http, ich habe mehrere Beispiele im Web gefunden. Ich habe es geschafft, diesen Code für eine POSTAnfrage zu erstellen :

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

alles funktioniert gut. Angenommen, ich möchte einen dritten Parameter an die POSTMethode übergeben, einen Parameter namens data. Der Datenparameter ist ein Objekt wie das folgende:

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

wie kann ich das machen ohne das zu erstellen KeyValuePair? Mein PHP RestAPIwartet auf eine JSON-Eingabe, daher FormUrlEncodedContentsollte der rawJSON korrekt gesendet werden. Aber wie kann ich das machen Microsoft.Net.Http? Vielen Dank.

IlDrugo
quelle
Wenn ich Ihre Frage verstehe, möchten Sie JSON-Inhalte anstelle von formularcodierten Inhalten senden (und als Erweiterung möchten Sie, dass Ihr anonymer Typ als JSON in diesen Inhalt serialisiert wird)?
CodingGorilla
@CodingGorilla yes ist ein anonymer Typ.
IlDrugo
3
Verwenden Sie als Randnotiz für zukünftige Leser kein a usingfür die HttpClient. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
2
Hinweis von Microsoft, warum usingnicht verwendet werden sollte: 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

Antworten:

151

Die direkte Antwort auf Ihre Frage lautet: Nein. Die Signatur für die PostAsyncMethode lautet wie folgt:

öffentliche Aufgabe PostAsync (Uri requestUri, HttpContent-Inhalt)

Während Sie also ein übergeben können object, PostAsyncmuss es vom Typ sein HttpContentund Ihr anonymer Typ erfüllt diese Kriterien nicht.

Es gibt jedoch Möglichkeiten, das zu erreichen, was Sie erreichen möchten. Zunächst müssen Sie Ihren anonymen Typ in JSON serialisieren. Das häufigste Tool hierfür ist Json.NET . Und der Code dafür ist ziemlich trivial:

var myContent = JsonConvert.SerializeObject(data);

Als nächstes müssen Sie ein Inhaltsobjekt erstellen, um diese Daten zu senden. Ich werde ein ByteArrayContentObjekt verwenden, aber Sie können einen anderen Typ verwenden oder erstellen, wenn Sie möchten.

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

Als Nächstes möchten Sie den Inhaltstyp festlegen, damit die API weiß, dass es sich um JSON handelt.

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

Dann können Sie Ihre Anfrage, die Ihrem vorherigen Beispiel sehr ähnlich ist, mit dem Formularinhalt senden:

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

Nebenbei bemerkt kann das Aufrufen der .ResultEigenschaft, wie Sie es hier tun, einige schlimme Nebenwirkungen haben, wie z. B. Dead Locking. Sie sollten also vorsichtig damit sein.

CodingGorilla
quelle
Okay, es ist sehr klar. Danke für diese Antwort. Nur eine Frage: Wenn a ausgeführt POST, PUT, DELETEwird, normalerweise die API-Rückgabe TRUE, habe ich die Methode als deklariert string, aber wenn ich dies tue: return result;Ich erhalte : Can't Convert HttpResponseMessage in string, sollte ich die Methodendeklaration ändern? Ich brauche die String-Antwort, weil ich sie nach einer anderen Klassenmethode deserialisieren muss.
IlDrugo
2
Wenn Sie den Hauptteil der Antwort deserialisieren müssen, result.Content.ReadAsStringAsync()ist es wahrscheinlich in Ordnung , die Zeichenfolge so zurückzugeben, wie Sie es in Ihrer Frage (mit ) getan haben. Abhängig von Ihrer Anwendungsstruktur ist es möglicherweise besser, das ContentObjekt direkt zurückzugeben, wenn Sie die Header überprüfen müssen, um festzustellen, um welchen Kongentyp es sich handelt (z. B. XML oder JSON). Wenn Sie jedoch wissen , dass JSON (oder ein anderes Format) immer zurückgegeben wird, sollte es in Ordnung sein, nur den Antworttext als Zeichenfolge zurückzugeben.
CodingGorilla
Tut mir leid zu fragen, aber ob müssen Sie dies tun, wenn die Daten vom Typ sind StringContent?
MyDaftQuestions
1
@MyDaftQuestions Ich bin mir nicht ganz sicher, was Sie fragen, aber Sie können ein StringContentdirekt an übergeben, PostAsyncda es von abgeleitet ist HttpContent.
CodingGorilla
@CodingGorilla, das war , was ich frage. Vielen Dank :)
MyDaftQuestions
65

Sie müssen Ihre Daten im Anforderungshauptteil als Rohzeichenfolge übergeben und nicht FormUrlEncodedContent. Eine Möglichkeit besteht darin, es in eine JSON-Zeichenfolge zu serialisieren:

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

Jetzt müssen Sie nur noch die Zeichenfolge an die post-Methode übergeben.

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
quelle
Was ist stringContent? In meinem Fall ist der stringContentWert "\"\"". Ist das der richtige Wert?
Lieber Genosse
Ist es möglich, das String-Ergebnis in vb aus Ihrem c # -Code zu erhalten? Ich fand heraus, dass es ziemlich ähnlich ist ...
Gumuruh
40

Eine einfache Lösung ist die Verwendung Microsoft ASP.NET Web API 2.2 Clientvon NuGet .

Dann können Sie dies einfach tun und das Objekt wird in JSON serialisiert und der Content-TypeHeader wird auf Folgendes gesetzt 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
quelle
2
definitiv ist PostAsJsonAsync da
Kat Lim Ruiz
24

Es gibt jetzt einen einfacheren Weg mit .NET Standardoder .NET Core:

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

HINWEIS: Um die JsonMediaTypeFormatterKlasse verwenden zu können, müssen Sie das Microsoft.AspNet.WebApi.ClientNuGet-Paket installieren, das direkt oder über ein anderes wie z Microsoft.AspNetCore.App.

Mit dieser Signatur von HttpClient.PostAsynckönnen Sie jedes Objekt übergeben und das JsonMediaTypeFormatterkümmert sich automatisch um die Serialisierung usw.

Mit der Antwort können Sie HttpContent.ReadAsAsync<T>den Antwortinhalt auf den erwarteten Typ deserialisieren:

var responseObject = await response.Content.ReadAsAsync<MyResponseType>();
Ken Lyon
quelle
1
Welche Version von .net wird verwendet? Meine Version kann "Formatierung" im System.Net.Http-Namespace
TemporaryFix
1
@Programmatic Sie müssen .NET Standardoder verwenden .NET Core, wie ich bereits erwähnt habe. Vielleicht benutzt du .NET Framework? In meinem Projekt wird der JsonMediaTypeFormatter von hier geladen: C: \ Programme \ dotnet \ sdk \ NuGetFallbackFolder \ microsoft.aspnet.webapi.client \ 5.2.6 \ lib \ netstandard2.0 \ System.Net.Http.Formatting. dll
Ken Lyon
1
@Programmatic Wenn Sie bereits einen dieser Projekttypen verwenden, müssen Sie möglicherweise ein zusätzliches NuGet-Paket hinzufügen. Ich vergesse genau welche automatisch für mich enthalten waren. In meinem Fall war es Teil des Microsoft.AspNetCore.App NuGet-Pakets.
Ken Lyon
1
Ich habe .NET Core verwendet, aber ich glaube nicht, dass meine Lösung auf die neueste Version der Sprache c # eingestellt war. Ich habe aktualisiert und es hat funktioniert. Vielen Dank
TemporaryFix
1
@Programmatic Gern geschehen. Ich bin froh zu hören, dass du es zum Laufen gebracht hast! Ich habe meiner Antwort einen Hinweis zum NuGet-Paket hinzugefügt.
Ken Lyon