Как уже упоминалось в нескольких других постах (см. Ссылки ниже), я пытаюсь создать фильтры ответов для изменения содержимого, создаваемого другим веб-приложением.
У меня есть базовая логика преобразования строк, которая работает и инкапсулируетсяв фильтры, которые являются производными от общей базы FilterBase.Однако логика должна работать с полным контентом, а не с фрагментами контента.Поэтому мне нужно кэшировать порции по мере их записи и выполнять фильтр после завершения всех записей.
Как показано ниже, я создал новый ResponseFilter, производный от MemoryStream.При записи содержимое кэшируется в другой MemoryStream.На Flush полное содержимое, теперь находящееся в MemoryStream, преобразуется в строку, и логика фильтра включается. Измененное содержимое затем записывается обратно в исходный поток.
Однако при каждом втором запросе (в основномкогда новый фильтр создается поверх предыдущего), выполняется метод Flush предыдущего фильтра.На этом этапе происходит сбой приложения в методе _outputStream.Write (), поскольку _cachedStream пуст.
Порядок событий следующий:
- Первый запрос
- Вызывается метод записи
- Вызывается метод Flush
- Вызывается метод Close
- Вызывается метод Close
- При этомУкажите точку, приложение вернется, и отобразится правильное содержимое.
- Второй запрос
- Вызов метода Flush
- Сбой приложения в _outputStream.Write.ArgumentOutOfRangeException (offset).
- Продолжить через сбой (с помощью Visual Studio)
- Метод Close вызывается
У меня есть пара вопросов:
- Почему дважды вызывается метод Close?
- Почему вызывается Flush после вызова метода Closed?
- К приведенному ниже пункту Джея Flush может вызываться до того, как поток полностью прочитангде должна находиться логика фильтра?В тесном?В Flush, но с «if close»?
- Какова правильная реализация фильтра ответов, который работает со всем контентом сразу?
Примечание :Я испытываю точно такое же поведение (за исключением событий Close), если я не переопределяю метод Close.
public class ResponseFilter : MemoryStream
{
private readonly Stream _outputStream;
private MemoryStream _cachedStream = new MemoryStream(1024);
private readonly FilterBase _filter;
public ResponseFilter (Stream outputStream, FilterBase filter)
{
_outputStream = outputStream;
_filter = filter;
}
// Flush is called on the second, fourth, and so on, page request (second request) with empty content.
public override void Flush()
{
Encoding encoding = HttpContext.Current.Response.ContentEncoding;
string cachedContent = encoding.GetString(_cachedStream.ToArray());
// Filter the cached content
cachedContent = _filter.Filter(cachedContent);
byte[] buffer = encoding.GetBytes(cachedContent);
_cachedStream = new MemoryStream();
_cachedStream.Write(buffer, 0, buffer.Length);
// Write new content to stream
_outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
_cachedStream.SetLength(0);
_outputStream.Flush();
}
// Write is called on the first, third, and so on, page request.
public override void Write(byte[] buffer, int offset, int count)
{
// Cache the content.
_cachedStream.Write(buffer, 0, count);
}
public override void Close()
{
_outputStream.Close();
}
}
// Example usage in a custom HTTP Module on the BeginRequest event.
FilterBase transformFilter = new MapServiceJsonResponseFilter();
response.Filter = new ResponseFilter(response.Filter, transformFilter);
Ссылки: