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


102

我有一个想要使用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();
    });

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


您是否尝试过将对象的XML序列化版本提交给服务端点?
约书亚·德雷克

Answers:


132

泛型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 2013年

1
@Jaans Flurl.Http通过提供了一种简单/简短的方法PostUrlEncodedAsync
Todd Menier 2014年

16
请注意,您需要添加对System.Net.Http.Formatting的引用才能使用PostAsJsonAsyncPostAsXmlAsync
Pete

6
要使用PostAsJsonAcync,请添加NuGet包Microsoft.AspNet.WebApi.Client!
丹尼斯

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设为可为null的int(int?


1
这将从一个外部项目调用,在该项目中,我将没有对包含Widget对象的程序集的引用。我尝试创建一个包含正确属性的匿名类型对象,使用此方法对其进行序列化,然后以这种方式传递,但是出现500 Internal Server Error。它永远不会碰到网络api控制器方法。
indot_brad 2012年

哦-那么您需要将xml或json发布到webapi服务,它将反序列化它-同样,SendAsync,正在对该服务的对象进行序列化
dunston 2012年

1
刚刚作出了一个更新-我有testet的代码,但也有一些简单的代码,但我应该工作
DUNSTON

8
我收到“非泛型类型'System.Net.Http.HttpRequestMessage'不能与类型参数一起使用”。这仍然有效吗?
user10479 2012年

5
是的,第一个解决方案不再起作用:aspnetwebstack.codeplex.com/discussions/350492
Giovanni B,

74

请注意,如果您使用的是可移植类库,则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时。如何避免双重编码?
Krzysztof Morcinek 2014年

3
优秀@fabiano!这确实做到了。在这种类型的项目中,这两个额外的参数是必需的。
彼得·克莱恩

很好@PeterKlein!我在Web上的Microsoft文档中找不到此信息,因此可以帮助其他遇到相同问题的人。没有这个技巧,我的项目根本不会发送数据。
法比亚诺2014年

1
请注意,您可能还必须在每个stackoverflow.com/a/40375351/3063273
Matt Thomas

4

如果您需要其他答案中提到的便捷方法的类型,但需要可移植性(或者即使您不需要),则可以查看Flurl [披露:我是作者]。它(薄)包装了HttpClientJson.NET,并添加了一些流利的糖和其他东西,包括一些烘焙的测试助手

发布为JSON:

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

或网址编码:

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

FluGet在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上面给出的建议,这使事情发生了。
彼得·克莱因

2
VB.NET并不是最喜欢的:)
懒惰的编码者,2016年

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

如果像我这样的人真的不了解以上所有内容,那么我举一个简单的例子为我工作。如果您有一个Web API,其URL为“ http://somesite.com/verifyAddress ”,则它是一种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;
    }

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


这将发送HTTP PUT消息,而不是请求的POST。就像其他人所说的那样,PostAsJsonAsync它将以POST的形式发送require数据。
Zhaph-本·迪吉德(Ben Duguid)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.