Asp.Net Core Kestrel設定Request Body Size

前兩天同事利用Web Api Post上傳檔案碰到了Request body too large的問題,我想到過去的經驗就是因為預設值比較小,所以改一下設定應該就可以Work了。

Request body too large

Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Request body too large.
at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.OnReadStarting()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.TryStart()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.ConsumeAsync()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication1 application) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication1 application)

通常不會把每一個設定值都記在腦中,所以就搜尋了一下Kestrel Server的設定應該在appsettings.json檔案中如何調整,查到了設定MaxRequestBodySize的C#程式碼,但是翻了很多篇網路上的文章都沒有找到如何在設定檔裡面調整的方法…

從官方文件和一些文章中看到下面的json設定內容,所以推斷應該同樣在Limits底下加上MaxRequestBodySize和設定值就可以搞定,結果實際上試了之後發現竟然完全沒效…,雖然是有看到有些設定在Server啟動之後會變成唯讀的情況,之後修改的內容不會更新,不過我一直覺得加在appsettings.json裡面不就是在啟動的時候會載入的設定?

{
  "Kestrel": {
    "Limits": {
      "MaxConcurrentConnections": 100,
      "MaxConcurrentUpgradedConnections": 100
    },
    "DisableStringReuse": true
  }
}

從這裡的文件內容則是看到下面這麼一段話…,因為Limits的設定是在KestrelServerOptions底下,所以我一直認為應該是可以透過appsettings.json設定,無奈試了幾次之後都沒辦法正常,所以最後只好妥協認命的去改Code…

Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions and endpoint configuration are configurable from configuration providers. Set other Kestrel configuration in C# code.

其實程式碼也不難,作法是在KestrelServerOptions的Limit底下的MaxRequestBodySize設定大小,因為是nullable型態,所以也可以設定為null。

第一段的是官方文件中的程式碼,第二段則是我寫在ConsoleApp(Program.cs)裡的程式碼,在UseKestrel的Extension method中設定。
因為是跑在Container裡面的程式,在改Code之前已經在shell script中判斷是否有MAX_REQ_BODY_SIZE這個環境變數來調整appsettings.json的檔案內容,所以是透過判斷是否有MAX_REQ_BODY_SIZE這個環境變數來設定大小,如果沒有找到MAX_REQ_BODY_SIZE就設定100MB的預設值,比官方預設不到30MB的大小再大一些。

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxRequestBodySize = 100_000_000;
});
public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
               .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseKestrel(opt =>
                        {
                            long maxReqBodySize = 100 * 1024 * 1024;
                            if (Environment.GetEnvironmentVariable("MAX_REQ_BODY_SIZE") != null)
                            {
                                long.TryParse(Environment.GetEnvironmentVariable("MAX_REQ_BODY_SIZE"), out maxReqBodySize);
                            }
                            opt.Limits.MaxRequestBodySize = maxReqBodySize;
                        })
                        .UseStartup<Startup>();
                });
}

結果最後繞一圈多花了一天的時間還是乖乖的改了程式碼…

對了,上面這樣的作法是整個網站都設定相同大小的限制,或許會有一些其它額外的潛在風險,所以官方建議的作法是只在需要調整的Function加上RequestSizeLimit Attribute

參考資訊:

發佈留言