Làm thế nào để sử dụng System.Net.HttpClient để đăng một kiểu phức tạp?


102

Tôi có một loại phức hợp tùy chỉnh mà tôi muốn làm việc bằng cách sử dụng API Web.

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

Và đây là phương pháp điều khiển API web của tôi. Tôi muốn đăng đối tượng này như vậy:

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

Và bây giờ tôi muốn sử dụng System.Net.HttpClientđể gọi phương thức. Tuy nhiên, tôi không chắc loại đối tượng nào cần truyền vào PostAsyncphương thức và cách xây dựng nó. Đây là một số mã khách hàng mẫu.

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

Làm cách nào để tạo HttpContentđối tượng theo cách mà API web sẽ hiểu nó?


Bạn đã thử gửi một phiên bản được tuần tự hóa XML của đối tượng của mình tới điểm cuối dịch vụ chưa?
Joshua Drake

Câu trả lời:


132

Chung HttpRequestMessage<T>đã bị xóa . Điều này :

new HttpRequestMessage<Widget>(widget)

sẽ không còn hoạt động .

Thay vào đó, từ bài đăng này , nhóm ASP.NET đã bao gồm một số lệnh gọi mới để hỗ trợ chức năng này:

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

Vì vậy, mã mới ( từ dunston ) trở thành:

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
Đúng, nhưng nếu bạn không có quyền truy cập vào lớp Widget thì sao?
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 Danh sách KeyValuePair` mới ?
Jaans

1
@Jaans Flurl.Http cung cấp một cách đơn giản / ngắn qua PostUrlEncodedAsync.
Todd Menier,

16
Lưu ý rằng bạn cần thêm một tham chiếu tới System.Net.Http.Formatting để có thể sử dụng PostAsJsonAsynchoặcPostAsXmlAsync
Pete

6
Để sử dụng PostAsJsonAcync, hãy thêm gói NuGet Microsoft.AspNet.WebApi.Client !!
Dennis

99

SendAsyncThay vào đó, bạn nên sử dụng phương pháp này, đây là phương pháp chung, tuần tự hóa đầu vào cho dịch vụ

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

Nếu bạn không muốn tạo lớp cụ thể, bạn có thể tạo nó với FormUrlEncodedContentlớp

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

Lưu ý: bạn cần đặt id của mình thành int ( int?) nullable


1
Điều này sẽ được gọi từ một dự án bên ngoài, nơi tôi sẽ không có tham chiếu đến hợp ngữ có chứa đối tượng Widget. Tôi đã thử tạo một đối tượng được nhập ẩn danh có chứa các thuộc tính chính xác, tuần tự hóa nó bằng phương pháp này và chuyển nó theo cách đó, nhưng tôi gặp phải Lỗi 500 Internal Server Error. Nó không bao giờ truy cập vào phương pháp bộ điều khiển api web.
indot_brad

Oh - sau đó bạn cần phải đăng xml, hoặc json với dịch vụ WebAPI, và nó sẽ deserialize nó - nó cũng làm như vậy, SendAsync, được serializing đối tượng sử dụng dịch vụ
Dunston

1
Đã chỉ cần thực hiện một bản cập nhật - tôi có testet mã, nhưng với một số mã đơn giản hơn, nhưng tôi nên làm việc
Dunston

8
Tôi nhận được "Không thể sử dụng kiểu không chung chung 'System.Net.Http.HttpRequestMessage' với các đối số kiểu". điều này vẫn còn giá trị?
user10479

5
Vâng, giải pháp đầu tiên không hoạt động nữa: aspnetwebstack.codeplex.com/discussions/350492
Giovanni B

74

Lưu ý rằng nếu bạn đang sử dụng Thư viện lớp di động, HttpClient sẽ không có phương thức PostAsJsonAsync . Để đăng nội dung dưới dạng JSON bằng Thư viện lớp di động, bạn sẽ phải thực hiện điều này:

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

Khi argsAsJson đến từ một đối tượng được tuần tự hóa, và đối tượng này có một thuộc tính tức là. Content = "domain \ user", thì \ sẽ được mã hóa hai lần. Một lần khi được tuần tự hóa thành argsAsJson và lần thứ hai khi PostAsync đăng contentPost. Làm thế nào để tránh mã hóa kép?
Krzysztof Morcinek,

3
@Fabiano xuất sắc! Điều này thực sự đã thực sự lừa. Hai đối số bổ sung đó là cần thiết trong loại dự án này.
Peter Klein

Rất tốt @PeterKlein! Tôi không thể tìm thấy thông tin này trong tài liệu của Microsoft trên web, vì vậy thông tin này có thể giúp những người khác gặp vấn đề tương tự. Dự án của tôi chỉ đơn giản là không gửi dữ liệu nếu không có thủ thuật này.
Fabiano

1
Lưu ý rằng bạn cũng có thể phải thêm "application / json" vào tiêu đề Chấp nhận của yêu cầu, theo stackoverflow.com/a/40375351/3063273
Matt Thomas

4

Nếu bạn muốn các loại phương pháp tiện lợi được đề cập trong các câu trả lời khác nhưng cần tính di động (hoặc thậm chí nếu không), bạn có thể muốn xem Flurl [tiết lộ: Tôi là tác giả]. Nó (mỏng) bao bọc HttpClientvà Json.NET và thêm một số đường thông thạo và các chất bổ sung khác, bao gồm một số trợ giúp thử nghiệm nướng sẵn .

Đăng dưới dạng JSON:

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

hoặc được mã hóa URL:

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

Cả hai ví dụ trên đều trả về một HttpResponseMessage, nhưng Flurl bao gồm các phương thức mở rộng để trả về những thứ khác nếu bạn chỉ muốn cắt theo đuổi:

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

Flurl có sẵn trên NuGet:

PM> Install-Package Flurl.Http

1

Sau khi điều tra rất nhiều lựa chọn thay thế, tôi đã tìm ra một cách tiếp cận khác, phù hợp với phiên bản API 2.0.

(VB.NET là yêu thích của tôi, 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

Chúc may mắn! Đối với tôi, điều này đã thành công (cuối cùng!).

Trân trọng, Peter


1
Điều này VỚI những đề xuất được đưa ra ở trên bởi @Fabiano sẽ khiến mọi thứ xảy ra.
Peter Klein,

2
VB.NET không có những :) yêu thích
Lazy Coder

1

Tôi nghĩ bạn có thể làm điều này:

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

Trong trường hợp một người như tôi không thực sự hiểu tất cả những gì ở trên đang nói về điều gì, tôi đưa ra một ví dụ dễ hiểu đang phù hợp với tôi. Nếu bạn có một api web có url là " http://somesite.com/verifyAddress ", thì đó là một phương thức đăng và nó cần bạn chuyển cho nó một đối tượng địa chỉ. Bạn muốn gọi api này trong mã của mình. Đây là những gì bạn có thể làm.

    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

Đây là đoạn mã mà tôi đúc kết được, dựa trên các câu trả lời khác ở đây. Điều này dành cho một HttpPost nhận và phản hồi với các loại phức tạp:

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

Thực hiện một cuộc gọi dịch vụ như sau:

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

Và phương thức dịch vụ như thế này:

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync đảm nhận Serialization và deserialization qua mạng


Thao tác này sẽ gửi một thông báo HTTP PUT, không phải một BÀI ĐĂNG như được yêu cầu. Như những người khác đã nói PostAsJsonAsyncsẽ gửi dữ liệu yêu cầu, dưới dạng BÀI ĐĂNG trong JSON.
Zhaph - Ben Duguid
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.