Как передать объект HttpClient.PostAsync и сериализовать его как тело JSON?

94

Пользуюсь 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должен rawправильно отправлять json. Но как я могу это сделать Microsoft.Net.Http? Спасибо.

IlDrugo
источник
Если я понимаю ваш вопрос, вы хотите правильно отправлять содержимое JSON вместо содержимого, закодированного в форме (и, соответственно, вы хотите, чтобы ваш анонимный тип был сериализован как JSON в этот контент)?
CodingGorilla
@CodingGorilla yes - анонимный тип.
IlDrugo
3
В качестве примечания для будущих читателей не используйте 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/…
Ogglas

Ответы:

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свойства, как здесь, может иметь плохие побочные эффекты, например мертвую блокировку, поэтому будьте осторожны с этим.

КодированиеGorilla
источник
Хорошо, это очень ясно. Спасибо за этот ответ. Просто вопрос: когда POST, PUT, DELETEвыполняется, обычно возвращается API TRUE, я объявил метод как string, но когда я это сделаю: return result;я получаю:, Can't Convert HttpResponseMessage in stringследует ли мне изменить объявление метода? Мне нужен строковый ответ, потому что мне нужно будет десериализовать его после в другом методе класса.
IlDrugo
2
Если вам нужно десериализовать тело ответа, то возврат строки в том виде, в котором вы указали в своем вопросе (используя result.Content.ReadAsStringAsync()), вероятно, вполне подойдет. В зависимости от структуры вашего приложения, может быть лучше вернуть Contentобъект напрямую, если вам нужно проверить заголовки, чтобы определить тип содержимого (например, XML или JSON). Но если вы знаете, что он всегда будет возвращать JSON (или какой-либо другой формат), тогда просто вернуть тело ответа в виде строки должно быть нормально.
CodingGorilla,
Извините за вопрос, но нужно ли вам это делать, если данные имеют тип StringContent?
MyDaftQuestions
1
@MyDaftQuestions Я не совсем понимаю, о чем вы спрашиваете, но вы можете передать StringContentнапрямую, PostAsyncпоскольку он происходит от HttpContent.
CodingGorilla 03
@CodingGorilla, что было то , что я просил. Спасибо :)
MyDaftQuestions 04
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);
Elolos
источник
Что есть stringContent? В моем случае stringContentзначение равно "\"\"". Это правильное значение?
R15
возможно ли получить строковый результат в vb из вашего кода С #? я обнаружил, что это очень похоже ....
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);
Trydis
источник
2
определенно PostAsJsonAsync можно использовать
Кэт Лим Руиз
26

Теперь есть более простой способ : .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. Я забываю, какие именно были включены для меня автоматически. В моем случае он был включен в пакет NuGet Microsoft.AspNetCore.App.
Кен Лайон
1
Я использовал .NET Core, но не думаю, что мое решение было настроено на использование последней версии языка C #. Я обновился, и все заработало. Спасибо
TemporaryFix
1
@Programmatic Пожалуйста. Я рад слышать, что у вас все работает! К своему ответу я добавил примечание о пакете NuGet.
Кен Лайон