You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kestrel在FIN及请求结束后出现BadHttpRequestException问题求助

.NET 6 Minimal API 出现 BadHttpRequestException(请求已正常返回200)的解决建议

问题背景

基于.NET 6构建的Minimal API部署在Docker容器并托管于AWS,客户端为Wordpress,通过PHP Curl调用API。上线后日志中频繁出现Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content异常,但请求实际已正常处理并返回200状态码,且该异常无法被自定义异常中间件捕获。

异常日志

1 "Connection id \"{ConnectionId}\" accepted.",
2 "Connection id \"{ConnectionId}\" started.",
3 "{HostingRequestStartingLog:l}",
4 "{CandidateCount} candidate(s) found for the request path '{Path}'",
5 "Request matched endpoint '{EndpointName}'",
6 "The request has an origin header: '{origin}'.",
7 "CORS policy execution successful.",
8 "Successfully validated the token.",
9 "AuthenticationScheme: {AuthenticationScheme} was successfully authenticated.",
10"AuthenticationScheme: {AuthenticationScheme} was successfully authenticated.",
11"Authorization was successful.",
12"Executing endpoint '{EndpointName}'",
13"Writing value of type '{Type}' with status code '200'.",
14"Executed endpoint '{EndpointName}'",
15"HTTP {RequestMethod} {RequestPath} responded 200 in {Elapsed:0.0000} ms",
16"Connection id \"{ConnectionId}\" completed keep alive response.",
17"{HostingRequestFinishedLog:l}",
18"Connection id \"{ConnectionId}\" received FIN.",
19"Connection id \"{ConnectionId}\", Request id \"{TraceIdentifier}\": started reading request body.",
20"Connection id \"{ConnectionId}\" sending FIN because: \"{Reason}\"",
21"Connection id \"{ConnectionId}\" disconnecting.",
22"Connection id \"{ConnectionId}\" bad request data: \"{message}\"",
23"Exception": "Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content.\n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.TryReadInternal(ReadResult& readResult)\n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.OnConsumeAsync()"
24"Connection id \"{ConnectionId}\", Request id \"{TraceIdentifier}\": done reading request body.",
25"Connection id \"{ConnectionId}\" stopped.",

正常日志

1 "Connection id \"{ConnectionId}\" accepted.",
2 "Connection id \"{ConnectionId}\" started.",
3 "{HostingRequestStartingLog:l}",
4 "{CandidateCount} candidate(s) found for the request path '{Path}'",
5 "Request matched endpoint '{EndpointName}'",
6 "The request has an origin header: '{origin}'.",
7 "CORS policy execution successful.",
8 "Successfully validated the token.",
9 "AuthenticationScheme: {AuthenticationScheme} was successfully authenticated.",
10"AuthenticationScheme: {AuthenticationScheme} was successfully authenticated.",
11"Authorization was successful.",
12"Executing endpoint '{EndpointName}'",
13"Writing value of type '{Type}' with status code '200'.",
14"Executed endpoint '{EndpointName}'",
15"HTTP {RequestMethod} {RequestPath} responded 200 in {Elapsed:0.0000} ms",
16"Connection id \"{ConnectionId}\" completed keep alive response.",
17"{HostingRequestFinishedLog:l}",
18"Connection id \"{ConnectionId}\", Request id \"{TraceIdentifier}\": started reading request body.",
19"Connection id \"{ConnectionId}\", Request id \"{TraceIdentifier}\": done reading request body.",
20"Connection id \"{ConnectionId}\" received FIN.",
21"Connection id \"{ConnectionId}\" disconnecting.",
22"Connection id \"{ConnectionId}\" sending FIN because: \"{Reason}\"",
23"Connection id \"{ConnectionId}\" stopped.",

核心问题分析

通过日志对比可以明确:

  1. 异常触发时机:请求已完全处理并返回200后,客户端提前发送FIN关闭连接,而Kestrel仍尝试复用该长连接读取下一个请求的请求体,导致读取失败抛出异常。
  2. 异常无法捕获原因:该异常属于Kestrel底层连接处理阶段的错误,不在ASP.NET Core请求处理管道范围内,因此自定义异常中间件无法拦截。

解决建议

一、客户端(PHP Curl)侧调整

  • 禁用长连接复用:在Curl请求中添加Connection: close请求头,或设置Curl选项CURLOPT_FORBID_REUSE => true,确保客户端收到响应后立即关闭连接,避免Kestrel等待下一个请求。
    示例代码:
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, "你的API地址");
    curl_setopt($curl, CURLOPT_HTTPHEADER, array('Connection: close'));
    // 其他Curl配置项
    $response = curl_exec($curl);
    curl_close($curl);
    
  • 确保资源正确释放:避免在curl_exec后立即销毁Curl资源,等待响应完全接收后再执行curl_close。

二、服务器(.NET 6)侧调整

  • 强制关闭长连接:添加全局中间件,对所有响应注入Connection: close头,主动告知客户端不要复用连接:
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Connection = "close";
        await next();
    });
    
  • 过滤无关异常日志:如果不需要监控这类不影响业务的异常,可在appsettings.json中调整日志级别,过滤该特定异常:
    {
      "Logging": {
        "LogLevel": {
          "Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException": "Warning"
        }
      }
    }
    
  • 缩短连接空闲超时:配置Kestrel的连接空闲超时,减少等待下一个请求的时长:
    builder.WebHost.ConfigureKestrel(options =>
    {
        options.Limits.IdleTimeout = TimeSpan.FromSeconds(30);
    });
    

内容的提问来源于stack exchange,提问作者Andreas Andersson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:25:18