【问题标题】:"Response has already started" exception in a bare-bones ASP.NET Core middleware project基本 ASP.NET Core 中间件项目中的“响应已开始”异常
【发布时间】:2019-08-18 13:28:41
【问题描述】:

我的问题基本上是这样的——鉴于此处显示的代码,管道中还有什么正在尝试启动对客户端的响应?我知道有关该异常的其他问题,但似乎在我的中间件之后运行的管道中的某些东西导致了异常 - 它不是由我的中间件引起的,我认为是我的方案的不同之处。

这是一个简单的 ASP.NET Core 3.0 WebSocket 回显服务器——没有 SignalR、没有 MVC、没有路由、没有静态页面支持等。除了处理套接字之外,当中间件看到对 @987654323 的请求时@ 它作为回显客户端发回一个简单的页面(一个硬编码的字符串)。

浏览器接收到内容就好了,我的异常处理程序没有触发(关键点),但是在我的中间件处理完请求后,ASP.NET Core 记录了异常:

无法设置状态码,因为响应已经开始。

代码非常少:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<WebSocketMiddleware>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        var webSocketOptions = new WebSocketOptions()
        {
            KeepAliveInterval = TimeSpan.FromSeconds(120),
            ReceiveBufferSize = 4 * 1024
        };
        app.UseWebSockets(webSocketOptions);
        app.UseMiddleware<WebSocketMiddleware>();
    }
}
public class WebSocketMiddleware : IMiddleware
{
    // fields/properties omitted
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            if (context.WebSockets.IsWebSocketRequest)
            {
                // omitted, socket upgrade works normally
            }
            else
            {
                if(context.Request.Headers["Accept"][0].Contains("text/html"))
                {
                    // this works but causes the exception later in the pipeline
                    await context.Response.WriteAsync(SimpleHtmlClient.HTML);
                }
                else
                {
                    // ignore other requests such as favicon
                }
            }
        }
        catch (Exception ex)
        { 
            // code omitted, never triggered 
        }
        finally
        {
            // exception happens here
            await next(context);
        }
    }
}

我认为问题可能是我使用了WriteAsync,但如果我还在其他地方设置了 HTTP 状态,没有其他输出,比如在catch 块中设置 HTTP 500,它似乎就会发生。如果我通过添加throw 作为中间件中的第一条语句来逐步处理故意引起的异常,它会到达finally,在该处发生“已启动”异常。

那么考虑到Startup 类,还有什么尝试在管道中产生输出?

编辑:堆栈跟踪,最后引用的第 91 行是 finally 块中的 await next(context)

System.InvalidOperationException:无法设置 StatusCode,因为响应已经开始。 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(字符串值) 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Http.DefaultHttpResponse.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Builder.ApplicationBuilder.c.b__18_0(HttpContext 上下文) 在 C:\Source\WebSocketExample\KestrelWebSocketServer\WebSocketMiddleware.cs:line 91 中的 KestrelWebSocketServer.WebSocketMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) 在 Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.c__DisplayClass5_1.d.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 应用程序)

【问题讨论】:

  • 一个问题,你为什么要向服务提供商注册中间件?以前从未见过有人这样做。您是否尝试过从配置服务委托中删除它? docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/…
  • 这就是他们所说的“工厂激活的中间件”。我个人不喜欢“魔术方法”,宁愿让我的中间件显式地实现一个接口(在这种情况下为IMiddleware)。 docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/…
  • 一旦您写入流,其他中间件将无法写入。如果您想写入并通过管道传递一个流,则需要替换流。
  • 你为什么还要顺流而下。如果您正在写入响应,您的意思不是要使管道短路吗?
  • 框架将添加自己的处理程序。在这种情况下,您需要在下次调用时进行更改。如果您正在写入响应,则应在此处结束它,而不是在管道中调用下一个。

标签: c# asp.net-core asp.net-core-middleware


【解决方案1】:

用户@Nkosi 的评论是正确的——当我的中间件能够完全处理请求时(或者将 HTTP 升级到 WS,或者发送回 echo 客户端 HTML),它不应该将上下文交给 next 委托.但是,在这个非常简单的示例中,其他请求(例如试图检索网站图标的浏览器)我的中间件没有做任何事情,所以解决方案是这样的:

finally
{
    if(!context.Response.HasStarted)
        await next(context);
}

【讨论】:

  • 在文档here 中多次提到它作为短路。还有一个关于在响应发送到客户端后不调用next.Invoke 的特定警告。
猜你喜欢
  • 2019-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 2019-11-10
  • 2018-08-20
  • 2016-11-12
  • 2020-10-29
相关资源
最近更新 更多