2015-09-01 2 views
7

Я пишу часть пользовательского промежуточного программного обеспечения OWIN для регистрации всех HTTP-запросов и их ответов. Я хотел бы «сопоставить» их с отслеживанием. Вот код:OWIN middleware для корреляции и регистрации запросов и ответов?

public class PacketTrackingMiddleware 
{ 
    private readonly AppFunc _next; 

    public PacketTrackingMiddleware(AppFunc next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(IDictionary<string, object> environment) 
    { 
     IOwinContext context = new OwinContext(environment);    
     var request = context.Request; 
     var response = context.Response; 

     //capture details about the caller identity 

     var identity = (request.User != null && request.User.Identity.IsAuthenticated) 
      ? request.User.Identity.Name 
      : "(anonymous)"; 

     var apiPacket = new ApiPacket 
     { 
      CallerIdentity = identity 
     }; 

     //buffer the request stream in order to intercept downstream reads 
     var requestBuffer = new MemoryStream(); 
     request.Body = requestBuffer; 

     //buffer the response stream in order to intercept downstream writes 
     var responseStream = response.Body; 
     var responseBuffer = new MemoryStream(); 
     response.Body = responseBuffer; 

     //add the "Http-Tracking-Id" response header 
     context.Response.OnSendingHeaders(state => 
     { 
      var ctx = state as IOwinContext; 
      if (ctx == null) return; 
      var resp = ctx.Response; 

      //adding the tracking id response header so that the user 
      //of the API can correlate the call back to this entry 
      resp.Headers.Add("http-tracking-id", new[] { apiPacket.TrackingId.ToString("d") }); 
     }, context); 

     //invoke the next middleware in the pipeline 
     await _next.Invoke(environment); 

     //rewind the request and response buffers to record their content 
     WriteRequestHeaders(request, apiPacket); 
     requestBuffer.Seek(0, SeekOrigin.Begin); 
     var requestReader = new StreamReader(requestBuffer); 
     apiPacket.Request = await requestReader.ReadToEndAsync(); 

     WriteResponseHeaders(response, apiPacket); 
     responseBuffer.Seek(0, SeekOrigin.Begin); 
     var reader = new StreamReader(responseBuffer); 
     apiPacket.Response = await reader.ReadToEndAsync(); 

     //write the apiPacket to the database 
     //await database.InsterRecordAsync(apiPacket); 
     System.Diagnostics.Debug.WriteLine("TrackingId: " + apiPacket.TrackingId); 

     //make sure the response we buffered is flushed to the client 
     responseBuffer.Seek(0, SeekOrigin.Begin); 
     await responseBuffer.CopyToAsync(responseStream); 
    } 
    private static void WriteRequestHeaders(IOwinRequest request, ApiPacket packet) 
    { 
     packet.Verb = request.Method; 
     packet.RequestUri = request.Uri; 
     packet.RequestHeaders = request.Headers; 
    } 
    private static void WriteResponseHeaders(IOwinResponse response, ApiPacket packet) 
    { 
     packet.StatusCode = response.StatusCode; 
     packet.ReasonPhrase = response.ReasonPhrase; 
     packet.ResponseHeaders = response.Headers; 
    } 
} 

У меня возникли проблемы с добавлением HTTP-отслеживания идентификатор в заголовке ответа (эти строки здесь).

context.Response.OnSendingHeaders(state => 
{ 
    var ctx = state as IOwinContext; 
    if (ctx == null) return; 
    var resp = ctx.Response; 

    resp.Headers.Add("http-tracking-id", new[] { apiPacket.TrackingId.ToString("d") }); 
}, context); 

При добавлении заголовка иногда я получаю эту ошибку:

HttpException was unhandled by user code.

Additional information: Server cannot append header after HTTP headers have been sent.


Edit 1: Я проверяю это, просто запустив API, который открывает хром на мой http://localhost:64051/ адрес , Если я просматриваю какой-либо фактический API (http://localhost:64051/api/Accounts/21067), например, я не получаю эту ошибку. Должен ли я как-то просто отправлять 404 назад при просмотре в корень сайта?

+0

Из того, что я прочитал, это может произойти, если что-то вне конвейера OWIN уже отправило заголовки. Возможно, устаревшая страница ASPX очищает и завершает ответ. – Sneal

ответ

1

Я думаю, что это может быть простое исправление. Попробуйте:

var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"]; 
responseHeaders["http-tracking-id"] = new[] {apiPacket.TrackingId.ToString("d")}; 

Но не в context.Response.OnSendingHeaders. Просто в строю с остальными.

+0

Это отлично работает. Огромное спасибо. Я не мог решить это для жизни меня, и оказалось, что это было довольно просто. Еще раз спасибо! – BBauer42

Смежные вопросы