如何使用 System.Net.HttpClient 发布复杂类型?

Posted

技术标签:

【中文标题】如何使用 System.Net.HttpClient 发布复杂类型?【英文标题】:How to use System.Net.HttpClient to post a complex type? 【发布时间】:2012-05-05 12:00:09 【问题描述】:

我有一个想要使用 Web API 处理的自定义复杂类型。

public class Widget

    public int ID  get; set; 
    public string Name  get; set; 
    public decimal Price  get; set; 

这是我的 Web 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();
    );

如何以 Web API 可以理解的方式创建 HttpContent 对象?

【问题讨论】:

您是否尝试过将对象的 XML 序列化版本提交到服务端点? 【参考方案1】:

您应该改用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 设置为可为空的 int (int?)

【讨论】:

这将从外部项目中调用,在该项目中我不会引用包含 Widget 对象的程序集。我尝试创建一个包含正确属性的匿名类型对象,使用此方法对其进行序列化,并以这种方式传递它,但我收到 500 Internal Server Error。它永远不会命中 web api 控制器方法。 哦 - 那么你需要将 xml 或 json 发布到 webapi 服务,它会反序列化它 - 它的作用相同,SendAsync 正在序列化服务的对象 刚刚进行了更新 - 我已经测试了代码,但使用了一些更简单的代码,但我应该可以工作 我收到“非泛型类型 'System.Net.Http.HttpRequestMessage' 不能与类型参数一起使用”。这仍然有效吗? 是的,第一个解决方案不再起作用:aspnetwebstack.codeplex.com/discussions/350492【参考方案2】:

通用 HttpRequestMessage&lt;T&gt; 已被删除。这个:

new HttpRequestMessage<Widget>(widget)

不再工作

相反,from this post,ASP.NET 团队包含了一些 new calls 来支持此功能:

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

所以,新代码 (from 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() );

【讨论】:

是的,但是如果您无权访问 Widget 类怎么办? 新的HttpClient.PostAsXXXAsync&lt;T&gt;( 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 Flurl.Http 通过PostUrlEncodedAsync 提供了一种简单/快捷的方式。 请注意,您需要添加对 System.Net.Http.Formatting 的引用才能使用PostAsJsonAsyncPostAsXmlAsync 要使用 PostAsJsonAcync,添加 NuGet 包 Microsoft.AspNet.WebApi.Client !!【参考方案3】:

请注意,如果您使用的是可移植类库,HttpClient 将没有 PostAsJsonAsync 方法。 要使用可移植类库将内容发布为 JSON,您必须这样做:

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来自一个序列化的对象,并且这个对象有一个属性ie。 Content = "domain\user",则 \ 将被编码两次。一次是序列化为 argsAsJson,第二次是 PostAsync 发布 contentPost。如何避免双重编码? 优秀的@f***o !这真的成​​功了。在这种类型的项目中,这两个额外的参数是必要的。 非常好@PeterKlein!我无法在 Microsoft 的 Web 文档中找到此信息,因此这可以帮助其他有相同问题的人。如果没有这个技巧,我的项目根本不会发送数据。 请注意,根据***.com/a/40375351/3063273,您可能还必须将“application/json”添加到请求的 Accept 标头中【参考方案4】:

如果您想要其他答案中提到的便捷方法类型但需要可移植性(或者即使您不需要),您可能需要查看Flurl [披露:我是作者]。它(薄薄地)包装了 HttpClient 和 Json.NET,并添加了一些流畅的糖和其他好东西,包括一些烘焙的 testing helpers。

以 JSON 格式发布:

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

或 URL 编码:

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

上面的两个例子都返回一个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

【讨论】:

【参考方案5】:

在研究了很多替代方案后,我遇到了另一种方法,适用于 API 2.0 版本。

(VB.NET 是我最喜欢的,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

祝你好运!对我来说,这成功了(最终!)。

问候, 彼得

【讨论】:

这与@F***o 上面给出的建议一起使事情发生。 VB.NET 没有人喜欢 :)【参考方案6】:

我认为你可以这样做:

var client = new HttpClient();
HttpContent content = new Widget();
client.PostAsync<Widget>("http://localhost:44268/api/test", content, new FormUrlEncodedMediaTypeFormatter())
    .ContinueWith((postTask) =>  postTask.Result.EnsureSuccessStatusCode(); );

【讨论】:

【参考方案7】:

像这样进行服务调用:

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 负责网络上的序列化和反序列化

【讨论】:

这将发送 HTTP PUT 消息,而不是请求的 POST。正如其他人所说,PostAsJsonAsync 将发送所需的数据,作为 JSON 中的 POST。【参考方案8】:

这是我根据此处的其他答案得出的代码。这是用于接收和响应复杂类型的 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));

【讨论】:

【参考方案9】:

如果像我这样的人并不真正理解以上所有内容,我会举一个对我有用的简单示例。 如果你有一个 url 为“http://somesite.com/verifyAddress”的 web api,它是一个 post 方法,它需要你传递一个地址对象。你想在你的代码中调用这个 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;
    

【讨论】:

以上是关于如何使用 System.Net.HttpClient 发布复杂类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等

如何使用 Math.Net 连接矩阵。如何使用 Math.Net 调用特定的行或列?

WSARecv 如何使用 lpOverlapped?如何手动发出事件信号?