2014-08-30 4 views
5

들어오는 모든 요청과 응답을 데이터베이스에 기록하는 Owin midleware 구성 요소를 작성하려고합니다.요청/응답 로깅을위한 응답 본문

나는 그럭저럭 얻을 수 있었다.

나는 response.body를 읽으 려 고 막혔다. 덧붙여 :

스트림은 읽기를 지원하지 않습니다.

어떻게 Response.Body를 읽을 수 있습니까?

public class LoggingMiddleware : OwinMiddleware 
{ 
     private static Logger log = LogManager.GetLogger("WebApi"); 

     public LoggingMiddleware(OwinMiddleware next, IAppBuilder app) 
      : base(next) 
     { 
     } 

    public override async Task Invoke(IOwinContext context) 
    { 
     using (var db = new HermesEntities()) 
     { 

      var sw = new Stopwatch(); 
      sw.Start(); 

      var logRequest = new log_Request 
      { 
       Body = new StreamReader(context.Request.Body).ReadToEndAsync().Result, 
       Headers = Json.Encode(context.Request.Headers), 
       IPTo = context.Request.LocalIpAddress, 
       IpFrom = context.Request.RemoteIpAddress, 
       Method = context.Request.Method, 
       Service = "Api", 
       Uri = context.Request.Uri.ToString(), 
       UserName = context.Request.User.Identity.Name 

      }; 
      db.log_Request.Add(logRequest); 
      context.Request.Body.Position = 0; 

      await Next.Invoke(context); 

      var mem2 = new MemoryStream(); 
      await context.Response.Body.CopyToAsync(mem2); 

      var logResponse = new log_Response 
      { 
       Headers = Json.Encode(context.Response.Headers), 
       Body = new StreamReader(mem2).ReadToEndAsync().Result, 
       ProcessingTime = sw.Elapsed, 
       ResultCode = context.Response.StatusCode, 
       log_Request = logRequest 
      }; 

      db.log_Response.Add(logResponse); 

      await db.SaveChangesAsync(); 
     } 
    } 
} 
+0

이 작업을 받으셨어요? –

+0

일종의, 어리석은 work arounds와 더불어, 그러나 내가 처음에 원했던 것에 따라. 간단히 - 아니 – Marty

+2

이것은 나를 위해 그것을 해결 : http://stackoverflow.com/questions/26214113/how-can-i-safely-intercept-the-response-stream-in-a-custom-owin-middleware –

답변

7

응답 본문은 Katana 호스트의 경우 쓰기 전용 네트워크 스트림입니다. MemoryStream으로 바꾸고 스트림을 읽고 내용을 기록한 다음 메모리 스트림 내용을 원래 네트워크 스트림으로 다시 복사해야합니다. BTW, 미들웨어가 요청 본문을 읽는 경우 요청 본문이 버퍼링되지 않는 한 다운 스트림 구성 요소는이를 수행 할 수 없습니다. 따라서 요청 본문을 버퍼링하는 것을 고려해야 할 수도 있습니다. 일부 코드를 보려면 http://lbadri.wordpress.com/2013/08/03/owin-authentication-middleware-for-hawk-in-thinktecture-identitymodel-45/을 시작 지점으로 사용할 수 있습니다. 클래스 HawkAuthenticationHandler을보십시오.

+0

시체를 메모리 스트림으로 교체하려고 시도했지만 API가 작동을 멈 춥니 다. 결과가 리턴되지 않습니다. 어떤 아이디어? – Marty

+2

메모리 스트림의 내용을 네트워크 스트림으로 밀어 넣었는지 확인하십시오. 그렇지 않으면 클라이언트가 응답을받지 못합니다. 필자가 준 블로그 게시물에서 'TeardownCore'를 살펴보십시오. – Badri

7

응답 바디는 이러한 방식으로 기록 할 수 있습니다 :

public class LoggingMiddleware : OwinMiddleware 
{ 
    private static Logger log = LogManager.GetLogger("WebApi"); 

    public LoggingMiddleware(OwinMiddleware next, IAppBuilder app) 
     : base(next) 
    { 
    } 

    public override async Task Invoke(IOwinContext context) 
    { 
     using (var db = new HermesEntities()) 
     { 

      var sw = new Stopwatch(); 
      sw.Start(); 

      var logRequest = new log_Request 
      { 
       Body = new StreamReader(context.Request.Body).ReadToEndAsync().Result, 
       Headers = Json.Encode(context.Request.Headers), 
       IPTo = context.Request.LocalIpAddress, 
       IpFrom = context.Request.RemoteIpAddress, 
       Method = context.Request.Method, 
       Service = "Api", 
       Uri = context.Request.Uri.ToString(), 
       UserName = context.Request.User.Identity.Name 
      }; 

      db.log_Request.Add(logRequest); 
      context.Request.Body.Position = 0; 

      Stream stream = context.Response.Body; 
      MemoryStream responseBuffer = new MemoryStream(); 
      context.Response.Body = responseBuffer; 

      await Next.Invoke(context); 

      responseBuffer.Seek(0, SeekOrigin.Begin); 
      var responseBody = new StreamReader(responseBuffer).ReadToEnd(); 

      //do logging 

      var logResponse = new log_Response 
      { 
       Headers = Json.Encode(context.Response.Headers), 
       Body = responseBody, 
       ProcessingTime = sw.Elapsed, 
       ResultCode = context.Response.StatusCode, 
       log_Request = logRequest 
      }; 

      db.log_Response.Add(logResponse); 

      responseBuffer.Seek(0, SeekOrigin.Begin); 
      await responseBuffer.CopyToAsync(stream); 

      await db.SaveChangesAsync(); 
     } 
    } 
} 
1

나는 OWIN 환경 사전에 요청 본문을 작성 속성 조치를 적용하여 문제를 해결했습니다. 그런 다음 로깅 미들웨어는 키를 사용하여 액세스 할 수 있습니다. 미들웨어에서

public class LogResponseBodyInterceptorAttribute : ActionFilterAttribute 
{ 
    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) 
    { 
     if (actionExecutedContext?.Response?.Content is ObjectContent) 
     { 
      actionExecutedContext.Request.GetOwinContext().Environment["log-responseBody"] = 
       await actionExecutedContext.Response.Content.ReadAsStringAsync(); 
     } 
    } 
} 

그리고는 :

public class RequestLoggingMiddleware 
{ 
    ... 
    private void LogResponse(IOwinContext owinContext) 
    { 
     var message = new StringBuilder() 
      .AppendLine($"{owinContext.Response.StatusCode}") 
      .AppendLine(string.Join(Environment.NewLine, owinContext.Response.Headers.Select(x => $"{x.Key}: {string.Join("; ", x.Value)}"))); 

     if (owinContext.Environment.ContainsKey("log-responseBody")) 
     { 
      var responseBody = (string)owinContext.Environment["log-responseBody"]; 
      message.AppendLine() 
       .AppendLine(responseBody); 
     } 

     var logEvent = new LogEventInfo 
     { 
      Level = LogLevel.Trace, 
      Properties = 
      { 
       {"correlationId", owinContext.Environment["correlation-id"]}, 
       {"entryType", "Response"} 
      }, 
      Message = message.ToString() 
     }; 

     _logger.Log(logEvent); 
    } 
}