2017-06-14 94 views
0

我有Asp.Net.Core应用程序,其中必须有一些日志记录,今天我试图添加异常处理中间件。我已使用asp.net DiagonsticsUseExceptionHandler。问题是,当我的日志中间件是后ExceptionHandler中间件在Asp.Net Core中记录中间件,之后使用UseExceptionHandler

app.UseExceptionHandler("/error"); 

app.UseSerilogMiddleware(); 

这wasnot redirectig我到我的自定义错误页的时候有一些例外。

public async Task Invoke(HttpContext httpContext) 
     { 
      if (httpContext == null) 
      { 
       Log.Write(LogEventLevel.Fatal, "No HTTP context found!"); 
      } 

      HttpRequest request = httpContext.Request; 
      var logger = GetExtendedLogger(request); 

      var stopwatchStart = Stopwatch.GetTimestamp(); 
      try 
      { 
       var originalResponseBody = httpContext.Response.Body; 
       using (MemoryStream stream = new MemoryStream()) 
       { 
        **httpContext.Response.Body = stream;** 

        await _next(httpContext); 

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

        var elapsedMs = GetElapsedMilliseconds(stopwatchStart, Stopwatch.GetTimestamp()); 
        var statusCode = httpContext.Response?.StatusCode; 

        var level = GetLogEventLevel(statusCode); 

        var log = Log 
        .ForContext("RequestHeaders", httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true) 
        .ForContext("RequestHost", httpContext.Request.Host) 
        .ForContext("RequestProtocol", httpContext.Request.Protocol); 
        logger.Write(level, MessageTemplate, httpContext.Request.Method, httpContext.Request.Path, statusCode, elapsedMs, responseBody); 

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

      } 
      // Never caught, because LogException() returns false. 
      catch (Exception ex) when (AddExceptionLogEntry(logger, httpContext, GetElapsedMilliseconds(stopwatchStart, Stopwatch.GetTimestamp()), ex)) 
      { 
      } 
     }' 

这是记录中间件的一部分,但是我发现了什么是造成问题,但我没有解释它。问题是在这一行httpContext.Response.Body = stream;当我删除响应身体流到一切正常工作。

P.S.该记录方法是将文件从here

回答

1

TL粘贴; DR:不要写入流,UseExceptionHandler之前否则错误中间件不能写入重定向头。

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

在调用异常中间件之前,您正在写入响应流,因此重定向无法工作。 HTTP中的重定向是一个响应,它返回http代码301或302,并带有新的url的Location: http://example.com标题。

但是,当您写入流中时,标头已被刷新并且不能再被更改。

+0

我看到..但为什么删除'httpContext.Response.Body =流;'身体设置到新创建的流这一行修复了这个问题? –

+1

很简单:因为如果你移除了'httpContext.Response.Body = stream;',底层的中间件从不向内存流中写任何东西,所以内存流是** EMPTY **。当你返回到你的中间件时,'await stream.CopyToAsync(originalResponseBody);'也不要写任何东西,因为它包含0字节的数据,所以没有东西会通过线路发送到客户端,最终允许异常中间件写它的标题 – Tseng

+1

结论:您将不得不将中间件分成两部分。第一个使用带有AddExceptionLog的“try/catch”的中间件被注册** AFTER **异常中间件和另一个使用内存流的middlware并记录您放置的成功调用** BEFORE **异常中间件 – Tseng

相关问题