Bagaimana cara mengirimkan objek ke HttpClient.PostAsync dan membuat serial sebagai badan JSON?

94

Saya menggunakan System.Net.Http, saya menemukan beberapa contoh di web. Saya berhasil membuat kode ini untuk membuat POSTpermintaan:

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

semua bekerja dengan baik. Tetapi misalkan saya ingin meneruskan parameter ketiga ke POSTmetode, parameter dipanggil data. Parameter data adalah objek seperti ini:

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

bagaimana saya bisa melakukannya tanpa membuat KeyValuePair? Php saya RestAPImenunggu masukan json, jadi FormUrlEncodedContentharus mengirim rawjson dengan benar. Tapi bagaimana saya bisa melakukan ini Microsoft.Net.Http? Terima kasih.

IlDrugo
sumber
Jika saya memahami pertanyaan Anda, Anda ingin mengirim konten JSON alih-alih konten yang dikodekan dalam bentuk yang benar (dan dengan ekstensi Anda ingin tipe anonim Anda menjadi serial sebagai JSON ke dalam konten itu)?
CodingGorilla
@CodingGorilla ya adalah tipe anonim.
IlDrugo
3
Sebagai catatan tambahan untuk pembaca selanjutnya, jangan gunakan usinguntuk HttpClient. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
2
Catatan dari Microsoft mengapa usingtidak boleh digunakan: 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

Jawaban:

155

Jawaban langsung atas pertanyaan Anda adalah: Tidak. Tanda tangan untuk PostAsyncmetode ini adalah sebagai berikut:

public Task PostAsync (Uri requestUri, konten HttpContent)

Jadi, meskipun Anda dapat meneruskan objectke PostAsyncitu, haruslah tipe HttpContentdan tipe anonim Anda tidak memenuhi kriteria itu.

Namun, ada cara untuk mencapai apa yang ingin Anda capai. Pertama, Anda perlu membuat serial tipe anonim Anda ke JSON, alat yang paling umum untuk ini adalah Json.NET . Dan kode untuk ini cukup sepele:

var myContent = JsonConvert.SerializeObject(data);

Selanjutnya, Anda perlu membuat objek konten untuk mengirim data ini, saya akan menggunakan ByteArrayContentobjek, tetapi Anda dapat menggunakan atau membuat jenis yang berbeda jika Anda mau.

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

Selanjutnya, Anda ingin menyetel tipe konten agar API mengetahui bahwa ini adalah JSON.

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

Kemudian Anda dapat mengirim permintaan Anda sangat mirip dengan contoh Anda sebelumnya dengan isi formulir:

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

Di samping catatan, menelepon .Resultproperti seperti yang Anda lakukan di sini dapat memiliki beberapa efek samping yang buruk seperti dead locking, jadi Anda harus berhati-hati dengan ini.

CodingGorilla
sumber
Oke, sudah jelas. Terima kasih atas jawaban ini. Hanya sebuah pertanyaan: ketika POST, PUT, DELETEdilakukan, biasanya API kembali TRUE, saya menyatakan metode sebagai string, tetapi ketika saya melakukannya: return result;Saya mendapatkan:, Can't Convert HttpResponseMessage in stringharuskah saya mengubah deklarasi metode? Saya memerlukan respons string karena saya harus melakukan deserialisasi setelah di metode kelas lain.
IlDrugo
2
Jika Anda perlu mendesentralisasikan isi respons, mengembalikan string seperti yang Anda miliki dalam pertanyaan (menggunakan result.Content.ReadAsStringAsync()) mungkin tidak masalah. Bergantung pada struktur aplikasi Anda, mungkin lebih baik mengembalikan Contentobjek secara langsung jika Anda perlu memeriksa header untuk menentukan jenis konen (mis. XML atau JSON). Tetapi jika Anda tahu itu selalu akan mengembalikan JSON (atau format lain) maka hanya mengembalikan isi respons sebagai string seharusnya tidak apa-apa.
CodingGorilla
Maaf untuk bertanya, tetapi apakah Anda perlu melakukan ini jika datanya bertipe StringContent?
MyDaftQuestions
1
@MyDaftQuestions Saya tidak begitu yakin apa yang Anda tanyakan, tetapi Anda dapat meneruskan StringContentlangsung ke PostAsynckarena itu berasal dari HttpContent.
CodingGorilla
@CodingGorilla, bahwa adalah apa yang saya minta. Terima kasih :)
MyDaftQuestions
67

Anda harus meneruskan data Anda di badan permintaan sebagai string mentah, bukan FormUrlEncodedContent. Salah satu cara untuk melakukannya adalah dengan membuat serialnya menjadi string JSON:

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

Sekarang yang perlu Anda lakukan adalah meneruskan string ke metode posting.

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
sumber
Apa stringContent? Dalam kasus saya, stringContentnilainya adalah "\"\"". Apakah ini nilai yang benar?
R15
apakah mungkin untuk mendapatkan hasil string dalam vb dari kode c # Anda? saya menemukan itu sangat mirip ....
gumuruh
42

Solusi sederhana adalah dengan menggunakan Microsoft ASP.NET Web API 2.2 Clientdari NuGet .

Kemudian Anda cukup melakukan ini dan itu akan membuat serial objek ke JSON dan mengatur Content-Typeheader ke 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
sumber
2
pasti PostAsJsonAsync ada untuk digunakan
Kat Lim Ruiz
26

Sekarang ada cara yang lebih sederhana dengan .NET Standardatau .NET Core:

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

CATATAN: Untuk menggunakan JsonMediaTypeFormatterkelas tersebut, Anda perlu menginstal Microsoft.AspNet.WebApi.Clientpaket NuGet, yang dapat diinstal secara langsung, atau melalui yang lain seperti Microsoft.AspNetCore.App.

Dengan menggunakan tanda tangan ini HttpClient.PostAsync, Anda dapat mengirimkan objek apa pun dan JsonMediaTypeFormattersecara otomatis akan mengurus serialisasi, dll.

Dengan respons tersebut, Anda dapat menggunakan HttpContent.ReadAsAsync<T>untuk menghentikan konten respons ke jenis yang Anda harapkan:

var responseObject = await response.Content.ReadAsAsync<MyResponseType>();
Ken Lyon
sumber
1
versi .net apa yang digunakan ini? Versi saya tidak dapat menemukan "Pemformatan" di namespace System.Net.Http
TemporaryFix
1
@Programmatic Anda perlu menggunakan .NET Standardatau .NET Core, seperti yang saya sebutkan. Mungkin Anda menggunakan .NET Framework? Dalam proyek saya, JsonMediaTypeFormatter sedang dimuat dari sini: 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 Jika Anda sudah menggunakan salah satu dari jenis proyek tersebut, mungkin Anda perlu menambahkan paket NuGet tambahan. Saya lupa persis mana yang disertakan secara otomatis untuk saya. Dalam kasus saya, itu disertakan sebagai bagian dari paket Microsoft.AspNetCore.App NuGet.
Ken Lyon
1
Saya menggunakan .NET Core, tetapi saya rasa solusi saya tidak diatur untuk menggunakan versi terbaru dari bahasa c #. Saya memperbarui dan berhasil. Terima kasih
Sementara
1
@Programatic Sama-sama. Saya senang mendengar Anda berhasil! Saya telah menambahkan catatan ke jawaban saya tentang paket NuGet.
Ken Lyon