Читать тело запроса дважды

Я пытаюсь прочитать тело в промежуточном программном обеспечении для целей аутентификации, но когда запрос попадает в контроллер API, объект становится пустым, так как тело уже прочитано. Есть ли в любом случае вокруг этого. Я читаю тело как это в моем промежуточном программном обеспечении.

var buffer = new byte[ Convert.ToInt32( context.Request.ContentLength ) ];
await context.Request.Body.ReadAsync( buffer, 0, buffer.Length );
var body = Encoding.UTF8.GetString( buffer );

2 ответа

Решение

Если вы используете application/x-www-form-urlencoded или же multipart/form-dataМожете смело звонить context.Request.ReadFormAsync() несколько раз, поскольку он возвращает кэшированный экземпляр при последующих вызовах.

Если вы используете другой тип контента, вам придется вручную буферизовать запрос и заменить тело запроса перематываемым потоком, таким как MemoryStream, Вот как вы можете это сделать, используя встроенное промежуточное программное обеспечение (вам нужно скоро зарегистрировать его в своем конвейере):

app.Use(next => async context => {
    // Keep the original stream in a separate
    // variable to restore it later if necessary.
    var stream = context.Request.Body;

    // Optimization: don't buffer the request if
    // there was no stream or if it is rewindable.
    if (stream == Stream.Null || stream.CanSeek) {
        await next(context);

        return;
    }

    try {
        using (var buffer = new MemoryStream()) {
            // Copy the request stream to the memory stream.
            await stream.CopyToAsync(buffer);

            // Rewind the memory stream.
            buffer.Position = 0L;

            // Replace the request stream by the memory stream.
            context.Request.Body = buffer;

            // Invoke the rest of the pipeline.
            await next(context);
        }
    }

    finally {
        // Restore the original stream.
        context.Request.Body = stream;
    }
});

Вы также можете использовать BufferingHelper.EnableRewind() расширение, которое является частью Microsoft.AspNet.Http пакет: он основан на аналогичном подходе, но опирается на специальный поток, который начинает буферизацию данных в памяти и помещает все в временный файл на диске при достижении порога:

app.Use(next => context => {
    context.Request.EnableRewind();

    return next(context);
});

К вашему сведению: промежуточное программное обеспечение для буферизации, вероятно, будет добавлено в vNext в будущем.

Использование для упоминания PinPoint о EnableRewind

Startup.cs
using Microsoft.AspNetCore.Http.Internal;

Startup.Configure(...){
...
//Its important the rewind us added before UseMvc
app.Use(next => context => { context.Request.EnableRewind(); return next(context); });
app.UseMvc()
...
}

Затем в промежуточном программном обеспечении вы просто перематываете и перечитываете

private async Task GenerateToken(HttpContext context)
    {
     context.Request.EnableRewind();
     string jsonData = new StreamReader(context.Request.Body).ReadToEnd();
    ...
    }

Это работает с.Net Core 2.1 и выше.

Сегодня столкнулся с похожей проблемой. Короче говоря, с чем раньше работали

Body.Seek(0, SeekOrigin.Begin);

привело к сегодняшнему исключению, по крайней мере, в моем случае. Это произошло после того, как код был перенесен на последнюю версию.NET Core.

Обходной путь для меня заключался в том, чтобы добавить это:

app.Use(next => context => { context.Request.EnableBuffering(); return next(context);

Добавьте это перед настройкой контроллеров или MVC. Кажется, это добавлено как часть версии.NET Core 2.1.

Надеюсь, это кому-то поможет!

Ура и счастливого кодирования.

Другие вопросы по тегам