Bagaimana cara menggunakan System.Net.HttpClient untuk mengirim tipe kompleks?

102

Saya memiliki tipe kompleks khusus yang ingin saya kerjakan menggunakan API Web.

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

Dan inilah metode pengontrol API web saya. Saya ingin memposting objek ini seperti ini:

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

Dan sekarang saya ingin menggunakan System.Net.HttpClientuntuk memanggil metode tersebut. Namun, saya tidak yakin jenis objek apa yang akan diteruskan ke dalam PostAsyncmetode, dan bagaimana membangunnya. Berikut beberapa contoh kode klien.

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

Bagaimana cara membuat HttpContentobjek sedemikian rupa sehingga web API akan memahaminya?

indot_brad
sumber
Sudahkah Anda mencoba mengirimkan objek versi berseri XML ke titik akhir layanan?
Joshua Drake

Jawaban:

132

Generik HttpRequestMessage<T>telah dihapus . Ini:

new HttpRequestMessage<Widget>(widget)

tidak akan bekerja lagi .

Sebaliknya, dari posting ini , tim ASP.NET telah menyertakan beberapa panggilan baru untuk mendukung fungsionalitas ini:

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

Jadi, kode baru ( dari dunston ) menjadi:

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() );
Joshua Ball
sumber
1
Ya, tapi bagaimana jika Anda tidak memiliki akses ke kelas Widget?
contactmatt
13
Daftar 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 KeyValuePair` baru ?
Jaans
1
@Jaans Flurl.Http menyediakan cara sederhana / singkat melalui PostUrlEncodedAsync.
Todd Menier
16
Perhatikan bahwa Anda perlu menambahkan referensi ke System.Net.Http.Formatting agar dapat menggunakan PostAsJsonAsyncatauPostAsXmlAsync
Pete
6
Untuk menggunakan PostAsJsonAcync, tambahkan paket NuGet Microsoft.AspNet.WebApi.Client !!
Dennis
99

Anda harus menggunakan SendAsyncmetode ini, ini adalah metode umum, yang membuat serial input ke layanan

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

Jika Anda tidak ingin membuat kelas beton, Anda dapat membuatnya dengan FormUrlEncodedContentkelas tersebut

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

Catatan: Anda perlu membuat id Anda menjadi nullable int ( int?)

dunston
sumber
1
Ini akan dipanggil dari proyek eksternal, di mana saya tidak akan memiliki referensi ke rakitan yang berisi objek Widget. Saya mencoba membuat objek yang diketik secara anonim yang berisi properti yang benar, membuat serial menggunakan metode ini, dan meneruskannya seperti itu, tetapi saya mendapatkan 500 Internal Server Error. Itu tidak pernah mengenai metode pengontrol api web.
indot_brad
Oh - maka Anda perlu memposting xml, atau json ke layanan webapi, dan itu akan deserialize itu - itu melakukan hal yang sama, SendAsync, membuat serialisasi objek untuk layanan
dunston
1
Baru saja membuat pembaruan - saya telah menguji kodenya, tetapi dengan beberapa kode yang lebih sederhana, tetapi saya harus bekerja
dunston
8
Saya mendapatkan "Tipe non-generik 'System.Net.Http.HttpRequestMessage' tidak dapat digunakan dengan argumen tipe". apakah ini masih berlaku?
pengguna10479
5
Ya, solusi pertama tidak berfungsi lagi: aspnetwebstack.codeplex.com/discussions/350492
Giovanni B
74

Perhatikan bahwa jika Anda menggunakan Pustaka Kelas Portabel, HttpClient tidak akan memiliki metode PostAsJsonAsync . Untuk memposting konten sebagai JSON menggunakan Perpustakaan Kelas Portabel, Anda harus melakukan ini:

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());
Fabiano
sumber
Ketika argsAsJson berasal dari objek berseri, dan objek ini memiliki properti yaitu. Content = "domain \ user", maka \ akan dikodekan dua kali. Sekali ketika diserialkan ke argsAsJson dan kedua kalinya ketika PostAsync memposting contentPost. Bagaimana cara menghindari pengkodean ganda?
Krzysztof Morcinek
3
@Fabiano luar biasa! Ini benar-benar berhasil. Dua argumen tambahan tersebut diperlukan dalam jenis proyek ini.
Peter Klein
Sangat bagus @PeterKlein! Saya tidak dapat menemukan informasi ini dalam dokumentasi Microsoft melalui web, jadi ini dapat membantu orang lain dengan masalah yang sama. Proyek saya tidak mengirim data tanpa trik ini.
Fabiano
1
Perhatikan bahwa Anda mungkin juga harus menambahkan "application / json" ke dalam header Terima permintaan, per stackoverflow.com/a/40375351/3063273
Matt Thomas
4

Jika Anda menginginkan jenis metode kenyamanan yang disebutkan dalam jawaban lain tetapi memerlukan portabilitas (atau bahkan jika tidak), Anda mungkin ingin memeriksa Flurl [pengungkapan: Saya penulis]. Ini (tipis) membungkus HttpClientdan Json.NET dan menambahkan beberapa gula yang lancar dan barang lainnya, termasuk beberapa pembantu pengujian yang dipanggang .

Posting sebagai JSON:

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

atau URL-encoded:

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

Kedua contoh di atas mengembalikan HttpResponseMessage, tetapi Flurl menyertakan metode ekstensi untuk mengembalikan hal-hal lain jika Anda hanya ingin langsung saja:

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

Flurl tersedia di NuGet:

PM> Install-Package Flurl.Http
Todd Menier
sumber
1

Setelah menyelidiki banyak alternatif, saya menemukan pendekatan lain, cocok untuk versi API 2.0.

(VB.NET adalah favorit saya, sooo ...)

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

Semoga berhasil! Bagi saya ini berhasil (pada akhirnya!).

Salam, Peter

pengguna2366741
sumber
1
Ini DENGAN saran yang diberikan di atas oleh @Fabiano membuat segalanya terjadi.
Peter Klein
2
VB.NET bukan favorit siapa pun :)
Lazy Coder
1

Saya pikir Anda bisa melakukan ini:

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

Jika seseorang seperti saya tidak benar-benar mengerti apa yang dibicarakan semua di atas, saya memberikan contoh mudah yang berhasil untuk saya. Jika Anda memiliki api web dengan url " http://somesite.com/verifyAddress ", itu adalah metode posting dan Anda perlu mengirimkannya ke objek alamat. Anda ingin memanggil api ini dalam kode Anda. Di sini yang dapat Anda lakukan.

    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;
    }
pengguna3293338
sumber
0

Ini adalah kode yang saya akhiri, berdasarkan jawaban lain di sini. Ini untuk HttpPost yang menerima dan merespons dengan tipe kompleks:

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));
Lodlaiden
sumber
-1

Lakukan panggilan layanan seperti ini:

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

Dan metode Layanan seperti ini:

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync menangani Serialisasi dan deserialisasi melalui jaringan

Nitin Nayyar
sumber
Ini akan mengirim pesan HTTP PUT, bukan POST seperti yang diminta. Seperti yang dikatakan orang lain PostAsJsonAsyncakan mengirim data yang dibutuhkan, sebagai POST di JSON.
Zhaph - Ben Duguid