จะใช้ System.Net.HttpClient เพื่อโพสต์ประเภทที่ซับซ้อนได้อย่างไร


102

ฉันมีประเภทที่ซับซ้อนที่กำหนดเองซึ่งฉันต้องการใช้งานโดยใช้ Web API

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

และนี่คือวิธีควบคุม API เว็บของฉัน ฉันต้องการโพสต์วัตถุนี้ดังนี้:

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

และตอนนี้ฉันต้องการใช้System.Net.HttpClientเพื่อโทรไปยังเมธอด อย่างไรก็ตามฉันไม่แน่ใจว่าจะส่งออบเจ็กต์ประเภทใดไปยังPostAsyncเมธอดนี้และจะสร้างได้อย่างไร นี่คือตัวอย่างโค้ดไคลเอ็นต์

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

ฉันจะสร้างHttpContentออบเจ็กต์ในลักษณะที่ Web API จะเข้าใจได้อย่างไร


คุณได้ลองส่งอ็อบเจ็กต์ XML เวอร์ชันซีเรียลไลซ์ไปยังปลายทางบริการแล้วหรือยัง
Joshua Drake

คำตอบ:


133

ทั่วไปHttpRequestMessage<T>ได้รับการถอดออก นี้ :

new HttpRequestMessage<Widget>(widget)

จะไม่ทำงาน

แต่จากการโพสต์นี้ทีม ASP.NET ได้รวมบางสายใหม่เพื่อสนับสนุนการทำงานนี้:

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

ดังนั้นรหัสใหม่ ( จาก dunston ) จึงกลายเป็น:

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
ใช่ แต่ถ้าคุณไม่สามารถเข้าถึงคลาส Widget ล่ะ?
contactmatt

13
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` ใหม่?
Jaans

1
@Jaans Flurl.Httpให้วิธีง่ายๆ / สั้น ๆ ผ่านทางPostUrlEncodedAsync.
Todd Menier

16
โปรดทราบว่าคุณต้องเพิ่มการอ้างอิงถึง System.Net.Http.Formatting เพื่อให้สามารถใช้งานได้PostAsJsonAsyncหรือPostAsXmlAsync
Pete

6
ในการใช้ PostAsJsonAcync ให้เพิ่มแพ็คเกจ NuGet Microsoft.AspNet.WebApi.Client !!
Dennis

99

คุณควรใช้SendAsyncวิธีนี้แทนซึ่งเป็นวิธีการทั่วไปที่ทำให้ข้อมูลเข้ากับบริการเป็นอนุกรม

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

หากคุณไม่ต้องการสร้างคลาสที่เป็นรูปธรรมคุณสามารถสร้างFormUrlEncodedContentคลาสได้

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

หมายเหตุ: คุณต้องทำให้ id ของคุณเป็น nullable int ( int?)


1
สิ่งนี้จะถูกเรียกจากโปรเจ็กต์ภายนอกโดยที่ฉันจะไม่มีการอ้างอิงไปยังแอสเซมบลีที่มีวัตถุวิดเจ็ต ฉันพยายามสร้างออบเจ็กต์ที่พิมพ์โดยไม่ระบุตัวตนซึ่งมีคุณสมบัติที่ถูกต้องทำให้เป็นอนุกรมโดยใช้วิธีนี้และส่งผ่านด้วยวิธีนั้น แต่ฉันได้รับข้อผิดพลาด 500 เซิร์ฟเวอร์ภายใน ไม่เคยใช้วิธีควบคุม web api
indot_brad

โอ้ - จากนั้นคุณต้องโพสต์ xml หรือ json ไปยังบริการ webapi และมันจะ deserialize มันก็ทำเช่นเดียวกัน SendAsync กำลังทำให้วัตถุเป็นอนุกรมสำหรับบริการ
dunston

1
เพิ่งทำการอัปเดต - ฉันได้ทดสอบรหัสแล้ว แต่มีรหัสที่ง่ายกว่านี้ แต่ฉันควรใช้งานได้
dunston

8
ฉันได้รับ "System.Net.Http.HttpRequestMessage ประเภทที่ไม่ใช่ทั่วไปไม่สามารถใช้กับอาร์กิวเมนต์ประเภท" ยังใช้ได้ไหม
user10479

5
ใช่วิธีแก้ปัญหาแรกใช้ไม่ได้อีกต่อไป: aspnetwebstack.codeplex.com/discussions/350492
Giovanni B

74

โปรดทราบว่าหากคุณกำลังใช้ไลบรารีคลาสแบบพกพาHttpClient จะไม่ได้มีวิธีการ หากต้องการโพสต์เนื้อหาเป็น JSON โดยใช้ Portable Class Library คุณจะต้องทำสิ่งนี้:

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

เมื่อ argsAsJson มาจากวัตถุที่ทำให้เป็นอนุกรมและวัตถุนี้มีคุณสมบัติคือ Content = "domain \ user" จากนั้น \ จะถูกเข้ารหัสสองครั้ง เมื่อต่ออนุกรมกับ argsAsJson และครั้งที่สองเมื่อ PostAsync โพสต์ contentPost จะหลีกเลี่ยงการเข้ารหัสซ้ำได้อย่างไร
Krzysztof Morcinek

3
สุดยอด @fabiano! นี่เป็นเคล็ดลับจริงๆ อาร์กิวเมนต์พิเศษทั้งสองนี้มีความจำเป็นในโครงการประเภทนี้
Peter Klein

ดีมาก @PeterKlein! ฉันไม่พบข้อมูลนี้ในเอกสารของ Microsoft ทางเว็บดังนั้นสิ่งนี้สามารถช่วยผู้อื่นที่มีปัญหาเดียวกันได้ โครงการของฉันไม่ส่งข้อมูลโดยไม่มีเคล็ดลับนี้
Fabiano

1
โปรดทราบว่าคุณอาจต้องเพิ่ม "application / json" ลงในส่วนหัว Accept ของคำขอต่อstackoverflow.com/a/40375351/3063273
Matt Thomas

4

หากคุณต้องการวิธีอำนวยความสะดวกประเภทต่างๆที่กล่าวถึงในคำตอบอื่น ๆ แต่ต้องการความสะดวกในการพกพา (หรือแม้ว่าคุณจะไม่ทำ) คุณอาจต้องการดูFlurl [การเปิดเผยข้อมูล: ฉันเป็นผู้เขียน] มัน (บาง ๆ ) แรปHttpClientและ Json.NET และเพิ่มบางน้ำตาลได้อย่างคล่องแคล่วและสินค้าอื่น ๆ รวมทั้งบางส่วนอบในการทดสอบผู้ช่วยเหลือ

โพสต์เป็น JSON:

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

หรือเข้ารหัส URL:

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

ทั้งสองตัวอย่างข้างต้นส่งคืน an HttpResponseMessageแต่ Flurl มีวิธีการขยายสำหรับการส่งคืนสิ่งอื่น ๆ หากคุณต้องการตัดการไล่ล่า:

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

Flurl มีอยู่ใน NuGet:

PM> Install-Package Flurl.Http

1

หลังจากตรวจสอบทางเลือกมากมายฉันได้พบแนวทางอื่นซึ่งเหมาะกับเวอร์ชัน API 2.0

(VB.NET เป็นรายการโปรดของฉันเลย ... )

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

โชคดี! สำหรับฉันสิ่งนี้ได้ผล (ในที่สุด!)

ขอแสดงความนับถือปีเตอร์


1
สิ่งนี้ด้วยคำแนะนำที่ให้ไว้ข้างต้นโดย @Fabiano ทำให้สิ่งต่างๆเกิดขึ้น
Peter Klein

2
VB.NET ไม่มีใครชื่นชอบ :)
Lazy Coder

1

ฉันคิดว่าคุณสามารถทำได้:

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

ในกรณีที่มีคนอย่างฉันไม่เข้าใจจริงๆว่าข้างบนพูดถึงอะไรฉันขอยกตัวอย่างง่ายๆที่ใช้ได้ผลกับฉัน หากคุณมีเว็บ api ซึ่ง url คือ " http://somesite.com/verifyAddress " นั่นเป็นวิธีการโพสต์และคุณต้องส่งผ่านวัตถุที่อยู่ คุณต้องการเรียก api นี้ในรหัสของคุณ สิ่งที่คุณทำได้มีดังนี้

    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

นี่คือรหัสที่ฉันสรุปโดยอิงจากคำตอบอื่น ๆ ที่นี่ สำหรับ HttpPost ที่รับและตอบสนองด้วยประเภทที่ซับซ้อน:

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

โทรหาบริการดังนี้:

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

และวิธีการบริการดังนี้:

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync ดูแล Serialization และ deserialization ผ่านเครือข่าย


สิ่งนี้จะส่งข้อความ HTTP PUT ไม่ใช่ POST ตามที่ร้องขอ อย่างที่คนอื่นบอกว่าPostAsJsonAsyncจะส่งข้อมูลที่ต้องการเป็น POST ใน JSON
Zhaph - Ben Duguid
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.