Как использовать System.Net.HttpClient для публикации сложного типа?

102

У меня есть настраиваемый сложный тип, с которым я хочу работать с помощью веб-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объект таким образом, чтобы веб-API его понимал?

indot_brad
источник
Вы пытались отправить сериализованную версию объекта XML в конечную точку службы?
Джошуа Дрейк,

Ответы:

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

Итак, новый код ( от данстона ) становится:

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

Примечание: вам нужно сделать свой идентификатор обнуляемым int ( int?)

Dunston
источник
1
Это будет вызываться из внешнего проекта, где у меня не будет ссылки на сборку, содержащую объект Widget. Я попытался создать анонимно типизированный объект, содержащий правильные свойства, сериализовать его с помощью этого метода и передать его таким образом, но я получил 500 Internal Server Error. Он никогда не попадает в метод веб-контроллера api.
indot_brad
О, тогда вам нужно отправить xml или json в службу webapi, и он десериализует его - он делает то же самое, SendAsync, сериализует объект для службы
Данстон
1
Только что сделали обновление - я протестировал код, но с более простым кодом, но я должен работать
данстон
8
Я получаю сообщение «Неуниверсальный тип System.Net.Http.HttpRequestMessage нельзя использовать с аргументами типа». это все еще в силе?
user10479
5
Да, первое решение больше не работает: aspnetwebstack.codeplex.com/discussions/350492
Джованни Б.
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 поступает из сериализованного объекта, и этот объект имеет свойство, т.е. Content = "домен \ пользователь", то \ будет дважды закодирован. Один раз при сериализации в argsAsJson и второй раз, когда PostAsync публикует contentPost. Как избежать двойного кодирования?
Krzysztof Morcinek
3
Отлично @fabiano! Это действительно помогло. Эти два дополнительных аргумента необходимы в проектах такого типа.
Питер Кляйн
Очень хорошо @PeterKlein! Я не смог найти эту информацию в документации Microsoft в Интернете, так что это может помочь другим с той же проблемой. Мой проект просто не отправляет данные без этой уловки.
Фабиано
1
Обратите внимание, что вам также может потребоваться добавить «application / json» в заголовок Accept запроса, согласно stackoverflow.com/a/40375351/3063273
Мэтт Томас,
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);

Оба приведенных выше примера возвращают 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

Удачи! Для меня это сработало (в конце концов!).

С уважением, Питер

user2366741
источник
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;
    }
user3293338
источник
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 в JSON.
Zhaph - Бен Дугид