Замена потока ответов в промежуточном программном обеспечении ASP.NET Core 1.0

Я хочу написать пользовательское промежуточное программное обеспечение в моем проекте ASP.NET Core 1.0, который заменит поток Http Response исходного фреймворка на мой собственный, поэтому я смогу выполнять над ним операции чтения / поиска / записи (первые 2 невозможны на оригинальном поток) в следующем коде, т.е. в действиях или фильтрах.

Я начал со следующего кода:

public class ReplaceStreamMiddleware
{
    protected RequestDelegate NextMiddleware;

    public ReplaceStreamMiddleware(RequestDelegate next)
    {
        NextMiddleware = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {       
        using (var responseStream = new MemoryStream())
        {
            var fullResponse = httpContext.Response.Body;
            httpContext.Response.Body = responseStream;
            await NextMiddleware.Invoke(httpContext);
            responseStream.Seek(0, SeekOrigin.Begin);
            await responseStream.CopyToAsync(fullResponse);
        }   
    }
}

Проблема со следующим кодом заключается в том, что иногда fullResponse поток уже закрыт на момент вызова await responseStream.CopyToAsync(fullResponse); поэтому выдает исключение. Не удается получить доступ к закрытому потоку.

Это странное поведение легко наблюдать, когда я загружаю страницу в браузере, а затем обновляю ее до полной загрузки.

Я бы хотел знать:

  1. почему это происходит?
  2. как это предотвратить?
  3. мое решение хорошая идея или есть другой способ заменить поток ответа?

1 ответ

Решение

Исключение не исходит от вашего CopyToAsync, Это от одного из абонентов вашего кода:

Вы не восстанавливаете исходный поток ответов в HttpContext, Поэтому тот, кто вызывает ваше промежуточное ПО, получит обратно закрытое MemoryStream,

Вот некоторый рабочий код:

app.Use(async (httpContext, next) =>
{
    using (var memoryResponse = new MemoryStream())
    {
        var originalResponse = httpContext.Response.Body;
        try
        {
            httpContext.Response.Body = memoryResponse;

            await next.Invoke();

            memoryResponse.Seek(0, SeekOrigin.Begin);
            await memoryResponse.CopyToAsync(originalResponse);
        }
        finally
        {
            // This is what you're missing
            httpContext.Response.Body = originalResponse;
        }
    }
});

app.Run(async (context) =>
{
    context.Response.ContentType = "text/other";
    await context.Response.WriteAsync("Hello World!");
});
Другие вопросы по тегам