C# 中间件清除响应内容

C# 中间件清除响应内容,c#,httpresponse,asp.net-core-3.1,asp.net-core-middleware,C#,Httpresponse,Asp.net Core 3.1,Asp.net Core Middleware,在我的启动.Configure管道的顶部,我创建了一个自定义中间件,它处理我的asp net核心应用程序的错误,如下所示: public class ErrorHandlerMiddleware { private readonly RequestDelegate _next; public ErrorHandlerMiddleware(RequestDelegate next) { _next = next; } public asy

在我的
启动.Configure
管道的顶部,我创建了一个自定义中间件,它处理我的asp net核心应用程序的错误,如下所示:

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());
            }
        }
    }
}
然后,它将以来自成功请求的原始html代码和来自errorhandler中间件的html代码结束,如下图所示:

在浏览器控制台中,我的响应将按预期包含401
StatusCode
。但是页面显示了整个html代码,如图所示

因此,在我的错误处理中间件写入响应之前,我正在寻找一种清除当前响应内容的方法,但在我的案例中似乎没有任何效果

在调用
wait context.Response.WriteAsync()
之前,我尝试添加
context.Response.Clear()
,但出现以下错误:

System.InvalidOperationException:无法清除响应,它已开始发送

因此,我尝试按如下方式重置响应正文:
context.response.body=new MemoryStream()
,但这不再将错误处理代码写入我的响应,并且根本不会重置内容。仅显示请求中的原始代码

我还尝试了
context.Response.Body.SetLength(0)
,但这给了我以下错误:

System.NotSupportedException:不支持指定的方法


事实上,我的解决方案快用完了。如何在再次写入之前重置响应内容?

在通过包装的
RequestDelegate
调用管道的其余部分之前,请使用临时
MemoryStream
替换
Response
流。这将导致所有内部中间件(稍后在管道中注册的中间件)写入临时缓冲区。之后,使用您需要的任何逻辑来确定是应该输出自定义响应还是使用原始响应。如果是后者,则将临时缓冲区复制回原始响应流;否则,请编写自定义响应

公共异步任务调用(HttpContext上下文){
var writeCustomResponse=false;
var originalResponse=context.Response.Body;
var newResponse=newmemoryStream();
//设置为临时缓冲区,以便写入
context.Response.Body=newResponse;
试一试{
等待下一步(上下文);
} 
抓住{
//随便
} 
最后{
//设置流回和倒带临时缓冲区
context.Response.Body=原始响应;
newResponse.Position=0;
writeCustomResponse=true;//此处是您的逻辑
}
如果(写入响应){
wait context.Response.WriteAsync(“任意”);
}否则{
//将临时缓冲区复制到原始缓冲区
等待newResponse.CopyToAsync(context.Response.Body,context.RequestAborted);
}
}
为了演示,我省略了上面的一些代码,并对其进行了一些重构。您可以重新组织它以最适合您的需要——只需确保在开始写入之前重置
Response.Body


为了使其工作,您必须在管道中尽早注册中间件(即在
启动.配置
)。否则,外部中间件(在它之前注册的中间件)可能会在调用中间件之前开始写入响应,这正是我们需要避免的。

非常感谢,它现在工作得很好!
var response = await client.SendAsync(request);

//_contextAccessor.HttpContext.Response.StatusCode = (int)response.StatusCode;
_contextAccessor.HttpContext.Response.StatusCode = 401;