Прочитайте HttpContent у контролері WebApi


74

Як я можу прочитати вміст запиту PUT у дії контролера MVC webApi.

[HttpPut]
public HttpResponseMessage Put(int accountId, Contact contact)
{
    var httpContent = Request.Content;
    var asyncContent = httpContent.ReadAsStringAsync().Result;
...

Я отримую тут порожній рядок :(

Що мені потрібно зробити, це: з'ясувати, "які властивості" були змінені / надіслані в початковому запиті (це означає, що якщо Contactоб'єкт має 10 властивостей, і я хочу оновити лише 2 з них, я надсилаю і об'єкт лише з двома властивостями, щось на зразок цього:

{

    "FirstName": null,
    "LastName": null,
    "id": 21
}

Очікуваний кінцевий результат -

List<string> modified_properties = {"FirstName", "LastName"}

Відповіді:


138

За проектом вміст основного вмісту в веб-API ASP.NET трактується як потік лише для пересилання, який можна прочитати лише один раз.

Перше читання у вашому випадку виконується, коли веб-API прив'язує вашу модель, після чого Request.Contentнічого не поверне.

Ви можете видалити параметр contactз ваших дій, отримати вміст і десеріалізувати його вручну в об'єкт (наприклад, за допомогою Json.NET):

[HttpPut]
public HttpResponseMessage Put(int accountId)
{
    HttpContent requestContent = Request.Content;
    string jsonContent = requestContent.ReadAsStringAsync().Result;
    CONTACT contact = JsonConvert.DeserializeObject<CONTACT>(jsonContent);
    ...
}

Це повинно зробити трюк (припускаючи, що accountIdце параметр URL-адреси, тому він не буде розглядатися як прочитаний вміст).


Подяка. І так, ідентифікатор облікового запису - це параметр url.
Марті

1
Цікаво, чи не могли б ви створити MessageHandler, який закликав LoadIntoBuffer (), за вмістом запиту до того, як зв’язувач Моделі ввійшов.
Даррел Міллер,

@DarrelMiller Я не впевнений, що веб-API ASP.NET все одно буде прив'язувати модель - потребує перевірки.
tpeczek

Чи це спричинить глухий кут?
Airn5475

3
@ Airn5475 Як є, можливо. Вам слід переключити підпис дії на асинхронний і використовувати await.
tpeczek

19

Ви можете зберегти параметр CONTACT, використовуючи такий підхід:

using (var stream = new MemoryStream())
{
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
    context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    context.Request.InputStream.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}

Повернув для мене представлення json мого об'єкта параметра, щоб я міг використовувати його для обробки винятків та ведення журналу.

Знайдено в загальноприйнятому відповідь тут


4

Хоча це рішення може здатися очевидним, я просто хотів розмістити його тут, щоб наступний хлопець швидше його загуглив.

Якщо ви все ще хочете мати модель як параметр у методі, ви можете створити DelegatingHandlerбуфер вмісту.

internal sealed class BufferizingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        await request.Content.LoadIntoBufferAsync();
        var result = await base.SendAsync(request, cancellationToken);
        return result;
    }
}

І додайте його до глобальних обробників повідомлень:

configuration.MessageHandlers.Add(new BufferizingHandler());

Це рішення засноване на відповідь по Даррел Міллер .

Таким чином, всі запити будуть буферизовані.


Я думаю, що це може завадити моєму серверу ... але це може стати в нагоді.
Hunter-Orionnoir
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.