【发布时间】:2021-04-30 06:01:26
【问题描述】:
在我的Startup.Configure 管道的顶部,我创建了一个自定义中间件来处理我的asp net core 应用程序的错误,如下所示:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception)
{
if (context.Response.StatusCode != (int)HttpStatusCode.ServiceUnavailable && context.Response.StatusCode != (int)HttpStatusCode.InternalServerError)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
finally
{
if (context.Response.StatusCode / 100 != (int)HttpStatusCode.OK / 100)
{
string message = ((HttpStatusCode)context.Response.StatusCode) switch
{
HttpStatusCode.ServiceUnavailable => "The application is currently on maintenance. Please try again later.",
HttpStatusCode.Unauthorized => "You are not authorized to view this content.",
HttpStatusCode.NotFound => "The content you asked for doesn't exist.",
HttpStatusCode.InternalServerError => "There was an internal server error. Please try again in a few minutes.",
_ => "Something went wrong. Please verify your request or try again in a few minutes.",
};
StringBuilder sb = new StringBuilder();
sb.Append($"<html><head><title>Error {context.Response.StatusCode}</title></head><body>");
sb.AppendLine();
sb.Append($"<h1>Error {context.Response.StatusCode} : {(HttpStatusCode)context.Response.StatusCode}</h1>");
sb.AppendLine();
sb.Append($"<p>{message}</p>");
sb.AppendLine();
sb.Append("<br /><input style='display: block;' type='button' value='Go back to the previous page' onclick='history.back()'><br /><br />");
sb.AppendLine();
if (context.Response.StatusCode != (int)HttpStatusCode.Unauthorized && context.Response.StatusCode != (int)HttpStatusCode.NotFound)
{
int delay = 5; //Minutes
sb.Append($"<i>This page will be automatically reloaded every {delay} minutes</i>");
sb.AppendLine();
sb.Append("<script>setTimeout(function(){window.location.reload(1);}, " + (delay * 1000 * 60) + ");</script>");
sb.AppendLine();
}
sb.Append("</body></html>");
await context.Response.WriteAsync(sb.ToString());
}
}
}
}
我的错误处理系统主要依赖于Response.StatusCode。
基本上,它只是将整个代码过程包装在try/catch 语句中。如果捕获到错误,StatusCode 将变为 500。
我还有一些其他中间件,尤其是 MaintenanceMiddleware,它可以将 StatusCode 设置为 503,以指定 API 正在维护中。
我还有一个服务,它直接与 API 通信并修改 StatusCode,具体取决于它从 API 获得的响应。
因此,在向客户端发送响应之前,如果在过程中将StatusCode 修改为 2XX 状态代码以外的其他内容,则错误处理程序中间件会捕获响应并必须擦除其内容以编写一个简单的 html 代码,该代码显示一个错误信息。
为此,我使用await context.Response.WriteAsync() 方法将html 代码写入响应。如果应用程序正在维护中或在代码中发现错误,它的工作速度非常好。
但是如果没有错误或者没有中间件使请求短路,如果一切顺利但只有StatusCode 被更改,例如如果我的服务返回 401 Unauthorized 错误代码如下:
var response = await client.SendAsync(request);
//_contextAccessor.HttpContext.Response.StatusCode = (int)response.StatusCode;
_contextAccessor.HttpContext.Response.StatusCode = 401;
然后它将以来自成功请求的原始 html 代码结束,和来自错误处理程序中间件的 html 代码,如下图所示:
在浏览器控制台中,我的响应将按预期包含 401 StatusCode。但是页面显示了整个 html 代码,如图所示。
因此,我一直在寻找一种在错误处理中间件写入响应之前清除当前响应内容的方法,但似乎对我的情况没有任何作用。
我尝试在调用await context.Response.WriteAsync() 之前添加context.Response.Clear(),但出现以下错误:
System.InvalidOperationException:无法清除响应,它已经开始发送。
因此,我尝试将响应正文重置如下:context.Response.Body = new MemoryStream(),但这不再将错误处理代码写入我的响应,并且根本不会重置内容。仅显示来自请求的原始代码。
我也尝试使用context.Response.Body.SetLength(0),但这给了我以下错误:
System.NotSupportedException:不支持指定的方法。
实际上,我的解决方案已经用完了。如何在再次写入之前重置响应内容?
【问题讨论】:
标签: c# httpresponse asp.net-core-3.1 asp.net-core-middleware