.NET 6控制台应用中Kestrel调用WriteAsync触发NullReferenceException
HttpContext.Response.WriteAsync()触发NullReferenceException 我正在使用.NET 6开发Windows服务器,需要监听内网HTTP请求,因此选择了Kestrel。发送HTTP请求时,Kestrel能正常接收并让我在代码中处理请求,但调用HttpContext.Response.WriteAsync()发送响应时,会触发NullReferenceException。
堆栈跟踪如下:
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.CreateResponseHeader(Boolean appCompleted)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.InitializeResponseAsync(Int32 firstWriteByteCount)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.WriteAsync(ReadOnlyMemory`1 data, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.WriteAsync(HttpResponse response, String text, Encoding encoding, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.WriteAsync(HttpResponse response, String text, CancellationToken cancellationToken)
我的服务注册代码:
IPAddress address = settings.BindAddress == null ? IPAddress.Any : IPAddress.Parse(settings.BindAddress); services.Configure<KestrelServerOptions>(options => { options.Listen(address, settings.ListeningPort); }); services.AddSingleton<IServer, KestrelServer>(); services.AddSingleton<ITransportFactory, SocketTransportFactory>(); services.AddSingleton<IApplicationLifetime, ApplicationLifetime>(); services.AddTransient<IHttpContextFactory, HttpContextFactory>();
实现IHttpApplication的处理类:
public class HttpController : IHttpApplication<HttpContext> { private readonly IServer _server; private readonly ILogger<HttpController> _logger; private readonly IHttpContextFactory _httpContextFactory; public HttpController(IServer server, IHttpContextFactory httpContextFactory, ILogger<HttpController> logger) { _server = server; _httpContextFactory = httpContextFactory; _logger = logger; } public async Task StartAsync(CancellationToken cancellationToken) { await Task.Yield(); _server.StartAsync(this, cancellationToken); } public HttpContext CreateContext(IFeatureCollection contextFeatures) { return _httpContextFactory.Create(contextFeatures); } public void DisposeContext(HttpContext context, Exception exception) { _httpContextFactory.Dispose(context); } public async Task ProcessRequestAsync(HttpContext context) { _logger.LogInformation("{Method} Http request received: {Path}", context.Request.Method, context.Request.Path); try { if (context.Request.Path.StartsWithSegments("/hello")) { var resp = "Hello World!"; await context.Response.WriteAsync(resp); } } catch(Exception ex) { _logger.LogError("Error while managing HTTP {Method} request to {Path} : {message}", context.Request.Method, context.Request.Path, ex.Message); } } }
请问为何调用context.Response.WriteAsync()会触发这个NullReferenceException?
问题原因
你手动注册IServer、ITransportFactory等核心服务的方式跳过了ASP.NET Core的标准初始化流程,导致HttpContext.Response的底层依赖(比如HttpProtocol内部的响应头相关对象)未被正确实例化。Kestrel需要一系列内部服务和特性支持才能完成响应的构建与发送,手动注册单个服务无法满足这些依赖要求,最终调用WriteAsync时触发空引用异常。
修复步骤
不要手动注册Kestrel相关的核心服务,改用ASP.NET Core提供的标准主机配置方式,它会自动注入所有必要的依赖:
- 修改服务注册与启动逻辑,使用
Host.CreateDefaultBuilder搭配ConfigureWebHostDefaults来配置Kestrel:
var host = Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { IPAddress address = settings.BindAddress == null ? IPAddress.Any : IPAddress.Parse(settings.BindAddress); options.Listen(address, settings.ListeningPort); }); webBuilder.UseStartup<Startup>(); }) .Build(); await host.RunAsync();
- 创建Startup类,配置请求处理管道:
public class Startup { public void ConfigureServices(IServiceCollection services) { // 添加必要的ASP.NET Core基础服务 services.AddHttpContextAccessor(); services.AddRouting(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 配置请求管道,添加端点路由 app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.Map("/hello", async context => { await context.Response.WriteAsync("Hello World!"); }); }); } }
- 如果必须保留
IHttpApplication的实现,确保通过标准主机来启动,而非手动调用_server.StartAsync:
// 在Startup的Configure中可以替换为自定义的IHttpApplication app.UseHttpApplication(new HttpController(...));
关键说明
ASP.NET Core的主机系统会自动处理Kestrel的初始化、服务依赖注入、请求管道构建等流程,手动注册核心服务会破坏这种依赖链,导致底层对象未初始化。使用标准配置方式能确保HttpContext的请求和响应对象都拥有完整的依赖支持,避免空引用异常。
内容的提问来源于stack exchange,提问作者vbond007

