Как можно безопасно перехватить поток ответов в пользовательском промежуточном программном обеспечении Owin
Я пытаюсь написать простое промежуточное программное обеспечение OWIN, чтобы перехватить поток ответов. То, что я пытаюсь сделать, - это заменить исходный поток собственным классом на основе потока, где я смогу перехватывать записи в поток ответов.
Однако я сталкиваюсь с некоторыми проблемами, потому что не могу знать, когда ответ был полностью записан внутренними компонентами промежуточного программного обеспечения в цепочке. Dispose
переопределение потока никогда не вызывается. Поэтому я не знаю, когда пришло время выполнять мою обработку, что должно произойти в конце потока ответа.
Вот пример кода:
public sealed class CustomMiddleware: OwinMiddleware
{
public CustomMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var response = context.Response;
// capture response stream
var vr = new MemoryStream();
var responseStream = new ResponseStream(vr, response.Body);
response.OnSendingHeaders(state =>
{
var resp = (state as IOwinContext).Response;
var contentLength = resp.Headers.ContentLength;
// contentLength == null for Chunked responses
}, context);
// invoke the next middleware in the pipeline
await Next.Invoke(context);
}
}
public sealed class ResponseStream : Stream
{
private readonly Stream stream_; // MemoryStream
private readonly Stream output_; // Owin response
private long writtenBytes_ = 0L;
public ResponseStream(Stream stream, Stream output)
{
stream_ = stream;
output_ = output;
}
... // System.IO.Stream implementation
public override void Write(byte[] buffer, int offset, int count)
{
// capture writes to the response stream in our local stream
stream_.Write(buffer, offset, count);
// write to the real output stream
output_.Write(buffer, offset, count);
// update the number of bytes written
writtenBytes_ += count;
// how do we know the response is complete ?
// we could check that the number of bytes written
// is equal to the content length, but content length
// is not available for Chunked responses.
}
protected override void Dispose(bool disposing)
{
// we could perform our processing
// when the stream is disposed of.
// however, this method is never called by
// the OWIN/Katana infrastructure.
}
}
Как я упоминал в комментариях из приведенного выше кода, я могу придумать две стратегии, чтобы определить, завершен ли ответ.
а) Я могу записать количество байтов, записанных в поток ответов, и сопоставить их с ожидаемой длиной ответа. Однако в случае ответов, в которых используется кодирование передачи по частям, длина не известна.
б) я могу решить, что поток ответа завершен, когда Dispose
вызывается в потоке ответа. Однако инфраструктура OWIN/Katana никогда не вызывает Dispose в замененном потоке.
Я исследовал Opaque Streaming, чтобы увидеть, будет ли возможным использование манипулирования базовым протоколом HTTP, но я не вижу, поддерживает ли Katana Opaque Streaming или нет.
Есть ли способ добиться того, чего я хочу?
1 ответ
Я не думаю, что вам понадобится подклассифицированный поток, но вот как вы можете прочитать ответ. Просто убедитесь, что это промежуточное ПО является первым в конвейере OWIN, чтобы оно было последним для проверки ответа.
using AppFunc = Func<IDictionary<string, object>, Task>;
public class CustomMiddleware
{
private readonly AppFunc next;
public CustomMiddleware(AppFunc next)
{
this.next = next;
}
public async Task Invoke(IDictionary<string, object> env)
{
IOwinContext context = new OwinContext(env);
// Buffer the response
var stream = context.Response.Body;
var buffer = new MemoryStream();
context.Response.Body = buffer;
await this.next(env);
buffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(buffer);
string responseBody = await reader.ReadToEndAsync();
// Now, you can access response body.
Debug.WriteLine(responseBody);
// You need to do this so that the response we buffered
// is flushed out to the client application.
buffer.Seek(0, SeekOrigin.Begin);
await buffer.CopyToAsync(stream);
}
}
Кстати, насколько я знаю, исходя из OwinMiddleware
не считается хорошей практикой, потому что OwinMiddleware
специфично для катаны. Однако это не имеет ничего общего с вашей проблемой.