在 ASP.NET Core 中间件出现异常时获得空响应

Muh*_*zim 7 c# middleware asp.net-core

我正在尝试创建一个可以记录响应正文以及全局管理异常的中间件,我成功了。我的问题是我放在异常中的自定义消息没有显示在响应中。

中间件代码 01:

public async Task Invoke(HttpContext context)
{
    context.Request.EnableRewind();
        
    var originalBodyStream = context.Response.Body;
    using (var responseBody = new MemoryStream())
    {
        try
        {
            context.Response.Body = responseBody;
            await next(context);

            context.Response.Body.Seek(0, SeekOrigin.Begin);
            var response = await new StreamReader(context.Response.Body).ReadToEndAsync();
            context.Response.Body.Seek(0, SeekOrigin.Begin);

            // Process log
             var log = new LogMetadata();
             log.RequestMethod = context.Request.Method;
             log.RequestUri = context.Request.Path.ToString();
             log.ResponseStatusCode = context.Response.StatusCode;
             log.ResponseTimestamp = DateTime.Now;
             log.ResponseContentType = context.Response.ContentType;
             log.ResponseContent = response;
             // Keep Log to text file
             CustomLogger.WriteLog(log);

            await responseBody.CopyToAsync(originalBodyStream);
        }
        catch (Exception ex)
        {
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            var jsonObject = JsonConvert.SerializeObject(My Custom Model);
            await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
            return;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我这样编写中间件,我的自定义异常工作正常,但我无法记录我的响应正文。

中间件代码 02:

 public async Task Invoke(HttpContext context)
  {
    context.Request.EnableRewind();

    try
    {
        await next(context);
    }
    catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
        return;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的控制器操作:

    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        throw new Exception("Exception Message");
    }
Run Code Online (Sandbox Code Playgroud)

现在我想用我的中间件 01 显示我的异常消息,但它不起作用,但它在我的中间件 02 上工作。

所以我的观察是出现了读取上下文响应的问题。我的中间件 01 代码中是否遗漏了什么?

有没有更好的方法可以满足我的目的,即记录响应主体并全局管理异常?

Ron*_*n C 8

我认为您的意思是此代码并未将其响应发送给客户端。

 catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
        return;
    }
Run Code Online (Sandbox Code Playgroud)

这样做的原因是它await context.Response.WriteAsync(jsonObject, Encoding.UTF8);不是写入原始正文流,而是写入可查找的内存流。因此,在写入之后,您必须将其复制到原始流中。所以我相信代码应该是这样的:

 catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);

        context.Response.Body.Seek(0, SeekOrigin.Begin);    //IMPORTANT!
        await responseBody.CopyToAsync(originalBodyStream); //IMPORTANT!
        return;
    }
Run Code Online (Sandbox Code Playgroud)