解决ASP.NET Core在IIS下返回413时出现502.3错误的问题
这个问题我之前也碰到过,核心是Kestrel和IIS作为反向代理时的请求处理逻辑差异导致的:
当你通过[RequestSizeLimit]特性或IHttpMaxRequestBodySizeFeature设置Kestrel的最大请求体限制时,Kestrel在检测到请求体超过限制的瞬间会直接中断TCP连接,而不是正常返回413响应。
而IIS(包括IIS Express)作为反向代理,它期望后端的Kestrel服务返回标准的HTTP响应头和内容。一旦Kestrel突然中断连接,IIS无法识别这是“请求过大”的合法场景,会将其判定为后端服务异常,从而返回HTTP Error 502.3 - Bad Gateway错误。
这也是为什么你手动返回413时IIS能正常工作——因为这是一个标准的HTTP响应,IIS会正常转发给客户端。
针对这个问题,有两种可靠的解决思路:
1. 让IIS提前拦截过大请求(推荐)
在IIS层面配置请求大小限制,让IIS在请求到达Kestrel之前就拦截超过大小的请求,直接返回413响应。这样可以完全避免Kestrel触发连接中断的行为。
需要修改站点的web.config文件,添加requestLimits配置:
<configuration> <system.webServer> <security> <requestFiltering> <!-- 单位是字节,和你的Kestrel限制保持一致(1024) --> <requestLimits maxAllowedContentLength="1024" /> </requestFiltering> </security> </system.webServer> </configuration>
配置完成后,IIS会直接拦截超过1KB的请求,返回标准的413响应,不会再出现502.3错误。
2. 手动处理请求大小检查,避免Kestrel中断连接
如果你不想依赖IIS的配置,可以在代码层面手动检查请求体大小,自己返回413响应,而不是依赖Kestrel的内置限制。
修改你的控制器代码,先检查请求长度(如果客户端发送了Content-Length头),然后读取请求体时限制读取的字节数,判断是否超过限制:
[Route("api/values")] public class ValuesController : Controller { private const int MaxRequestSize = 1024; [HttpPost] public IActionResult Post() { // 先检查ContentLength(如果客户端提供了这个头) if (Request.ContentLength.HasValue && Request.ContentLength > MaxRequestSize) { return StatusCode(StatusCodes.Status413PayloadTooLarge); } string echo = string.Empty; try { using (StreamReader sr = new StreamReader(Request.Body)) { // 只读取MaxRequestSize字节,避免读取过多内容 char[] buffer = new char[MaxRequestSize]; int bytesRead = sr.Read(buffer, 0, MaxRequestSize); echo = new string(buffer, 0, bytesRead); // 如果读取后Stream还没到结尾,说明请求体超过了限制 if (!sr.EndOfStream) { return StatusCode(StatusCodes.Status413PayloadTooLarge); } } } catch (Exception ex) { // 捕获读取异常,统一返回413 return StatusCode(StatusCodes.Status413PayloadTooLarge); } return Ok(echo); } }
这种方式不管是Kestrel独立运行还是IIS代理,都能正常返回413响应,因为没有触发Kestrel的连接中断逻辑。
额外注意事项
如果你使用的是ASP.NET Core 3.1及以上版本,并且代码中使用了同步读取请求体的方式(比如StreamReader.ReadToEnd()),需要在Kestrel配置中允许同步IO:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); webBuilder.ConfigureKestrel(options => { options.AllowSynchronousIO = true; }); });
内容的提问来源于stack exchange,提问作者DarcyThomas

