Як передати об’єкт HttpClient.PostAsync і серіалізувати як тіло JSON?


93

Я використовую System.Net.Http, я знайшов кілька прикладів в Інтернеті. Мені вдалося створити цей код для POSTзапиту:

public static string POST(string resource, string token)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(baseUri);
        client.DefaultRequestHeaders.Add("token", token);

        var content = new FormUrlEncodedContent(new[]
        {
             new KeyValuePair<string, string>("", "")
        });

        var result = client.PostAsync("", content).Result;
        string resultContent = result.Content.ReadAsStringAsync().Result;
        return resultContent;
    }
 }

все працює нормально. Але припустимо, що я хочу передати третій параметр POSTметоду, який називається data. Параметр даних є таким об'єктом:

object data = new
{
    name = "Foo",
    category = "article"
};

як я можу це зробити, не створюючи KeyValuePair? Мій php RestAPIчекає введення json, тому файл FormUrlEncodedContentповинен надіслати rawjson правильно. Але як я можу це зробити Microsoft.Net.Http? Дякую.


Якщо я розумію ваше запитання, ви хочете надіслати вміст JSON замість закодованого вмісту у формі (і, за розширенням, ви хочете, щоб ваш анонімний тип був серіалізований як JSON до цього вмісту)?
CodingGorilla

@CodingGorilla yes - це анонімний тип.
IlDrugo

3
Як допоміжну примітку для майбутніх читачів, не використовуйте a usingдля HttpClient. aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty

2
Примітка від Microsoft, чому usingне слід використовувати: HttpClient is intended to be instantiated once and reused throughout the life of an application. The following conditions can result in SocketException errors: Creating a new HttpClient instance per request. Server under heavy load. Creating a new HttpClient instance per request can exhaust the available sockets. docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…
Огглас,

Відповіді:


155

Пряма відповідь на ваше запитання: Ні. Підпис PostAsyncметоду такий:

загальнодоступне завдання PostAsync (Uri requestUri, вміст HttpContent)

Таким чином, в той час як ви можете пройти objectдо PostAsyncнього повинен бути типу HttpContentі ваш анонімний тип не відповідає цим критеріям.

Однак є способи досягти того, що ви хочете досягти. По-перше, вам потрібно буде серіалізувати ваш анонімний тип до JSON, найпоширенішим інструментом для цього є Json.NET . І код для цього досить тривіальний:

var myContent = JsonConvert.SerializeObject(data);

Далі вам потрібно буде створити об’єкт вмісту для надсилання цих даних, я буду використовувати ByteArrayContentоб’єкт, але ви можете використовувати або створити інший тип, якщо хочете.

var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);

Далі ви хочете встановити тип вмісту, щоб повідомити API, що це JSON.

byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

Тоді ви можете надіслати свій запит, дуже схожий на ваш попередній приклад із вмістом форми:

var result = client.PostAsync("", byteContent).Result

Як примітка, виклик .Resultвласності, як ви робите тут, може мати деякі погані побічні ефекти, такі як глухий замок, тому ви хочете бути обережними з цим.


Гаразд, це дуже ясно. Дякую за цю відповідь. Просто питання: коли a POST, PUT, DELETEвиконується, як правило, API повертає TRUE, я оголосив метод як string, але коли я це роблю: return result;I get:, чи Can't Convert HttpResponseMessage in stringслід змінювати декларацію методу? Мені потрібна відповідь на рядок, тому що мені потрібно буде десеріалізувати її після використання в іншому методі класу.
IlDrugo

2
Якщо вам потрібно десериалізувати тіло відповіді, тоді повернення рядка так, як у вашому запитанні (з використанням result.Content.ReadAsStringAsync()), мабуть, цілком нормальне. Залежно від структури вашої програми, може бути краще повернути Contentоб'єкт безпосередньо, якщо вам потрібно перевірити заголовки, щоб визначити, який тип конента (наприклад, XML або JSON). Але якщо ви знаєте, що він завжди повертає JSON (або інший формат), тоді просто повернення тіла відповіді як рядка повинно бути нормальним.
CodingGorilla

Вибачте, що запитую, але якщо вам потрібно це зробити, якщо дані типу StringContent?
MyDaftQuestions

1
@MyDaftQuestions Я не зовсім впевнений, про що ти запитуєш, але ти можеш передати його StringContentбезпосередньо, PostAsyncоскільки воно походить від HttpContent.
CodingGorilla

@CodingGorilla, що було то , що я просив. Дякую :)
MyDaftQuestions

67

Вам потрібно передавати свої дані в тілі запиту як сирий рядок, а не FormUrlEncodedContent. Один із способів зробити це - серіалізувати його у рядок JSON:

var json = JsonConvert.SerializeObject(data); // or JsonSerializer.Serialize if using System.Text.Json

Тепер все, що вам потрібно зробити, це передати рядок методу post.

var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); // use MediaTypeNames.Application.Json in Core 3.0+ and Standard 2.1+

var client = new HttpClient();
var response = await client.PostAsync(uri, stringContent);

Що це таке stringContent? У моєму випадку stringContentзначення "\"\"". Це правильне значення?
R15

чи можна отримати результат рядка у vb з вашого коду c #? я дізнався, що це цілком схоже ....
gumuruh

42

Просте рішення полягає у використанні Microsoft ASP.NET Web API 2.2 Clientз NuGet .

Тоді ви можете просто зробити це, і це буде серіалізувати об'єкт в JSON і встановити Content-Typeзаголовок на application/json; charset=utf-8:

var data = new
{
    name = "Foo",
    category = "article"
};

var client = new HttpClient();
client.BaseAddress = new Uri(baseUri);
client.DefaultRequestHeaders.Add("token", token);
var response = await client.PostAsJsonAsync("", data);

2
безумовно, PostAsJsonAsync є тут для використання
Kat Lim Ruiz

25

Зараз є простіший спосіб з .NET Standardабо .NET Core:

var client = new HttpClient();
var response = await client.PostAsync(uri, myRequestObject, new JsonMediaTypeFormatter());

ПРИМІТКА: Для того, щоб використовувати JsonMediaTypeFormatterклас, вам потрібно буде встановити Microsoft.AspNet.WebApi.Clientпакет NuGet, який можна встановити безпосередньо або через інший, наприклад Microsoft.AspNetCore.App.

Використовуючи цей підпис HttpClient.PostAsync, ви можете передати будь-який об'єкт, і JsonMediaTypeFormatterвін автоматично подбає про серіалізацію тощо.

За допомогою відповіді ви можете використовувати HttpContent.ReadAsAsync<T>для десериалізації вмісту відповіді до типу, який ви очікуєте:

var responseObject = await response.Content.ReadAsAsync<MyResponseType>();

1
яку версію .net використовується? Моя версія не може знайти "Форматування" у просторі імен System.Net.Http
TemporaryFix

1
@Programmatic Вам потрібно використовувати .NET Standardабо .NET Core, як я вже згадував. Може ти використовуєш .NET Framework? У моєму проекті JsonMediaTypeFormatter завантажується звідси: C: \ Program Files \ dotnet \ sdk \ NuGetFallbackFolder \ microsoft.aspnet.webapi.client \ 5.2.6 \ lib \ netstandard2.0 \ System.Net.Http.Formatting. dll
Ken Lyon

1
@Programmatic Якщо ви вже використовуєте один із цих типів проектів, можливо, вам потрібно додати додатковий пакет NuGet. Я точно забуваю, які для мене були включені автоматично. У моєму випадку він був включений до складу пакета Microsoft.AspNetCore.App NuGet.
Ken Lyon,

1
Я використовував .NET Core, але не думаю, що моє рішення було налаштовано на використання останньої версії мови c #. Я оновив, і це спрацювало. Дякую
TemporaryFix

1
@Programmatic Ласкаво просимо. Мені приємно чути, що ви спрацювали! Я додав примітку до моєї відповіді щодо пакету NuGet.
Ken Lyon
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.