由于MinRequestBodyDataRate和连接不良

时间:2018-05-17 23:02:40

标签: c# asp.net-core kestrel-http-server asp.net-core-2.1

我有一个ASP.NET Core Web应用程序,它接受来自经过身份验证的用户的文件上传,以及一些错误处理,以便在发生异常时通知我(Exceptional)。我的问题是,由于用户通过移动设备在覆盖范围不可靠的区域访问应用程序,我会定期收到BadHttpRequestException的警报。我过去常常使用2.0,但是直到2.1中的异常描述更新,我才知道它与MinRequestBodyDataRate特别相关,或者它是可配置的。 我已经使用以下

获得了默认值(在5秒内240字节)
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseKestrel(options =>
            {
                options.Limits.MinRequestBodyDataRate = new MinDataRate(240.0, TimeSpan.FromSeconds(10.0));
            }) 
            .UseStartup<Startup>();

这会使等待客户端/浏览器的持续时间加倍,但我仍然会收到错误警报。我无法控制用户的不良接收,但我仍然希望尝试找到一种方法来缓解错误。我可以进一步扩展最小值,但如果可能的话,我不想为整个应用程序执行此操作。理想情况下,它是处理上传的特定路线/操作。我能够找到的文档表明可以使用IHttpMinRequestBodyDataRateFeature配置此最小值。我想过把它放在动作中,但我不认为动作甚至被调用,直到modelbinding上传整个文件才能将它绑定到我的参数。

public async Task<IActionResult> Upload(IFormFile file)
{
    var minRequestBodyDataRateFeature = HttpContext.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    minRequestBodyDataRateFeature.MinDataRate = new MinDataRate(240, TimeSpan.FromSeconds(30));
    myDocumentService.SaveFile(file);
}

这在某种程度上得到了缓解,因为我之前已经实现了分块上传,并且只在最后一个块出现时将文件保存到服务/数据库(在中间的临时位置逐渐构建文件)。但即使通过BadHttpRequestException延长了持续时间,我仍然可以得到这些CreateWebHostBuilder(因为它没有相关性,所以没有显示分块)。

我认为最好的选择可能是尝试在设置中间件的配置方法中连接它,但我不确定仅将其应用于一个操作的最佳方法。可能是网址?而且我想知道如果我通过将最低费率设置为null来完全禁用最低费率会有什么影响。

我基本上只是希望连接能够更好地容忍质量差的连接,同时不会为应用程序的其他方面造成太多的烦恼。

  1. 我需要一种方法来应用更改(可能在中间件Startup.Configure()中),这样它只适用于受影响的路由/网址/操作。
  2. 如果我为该路由/ url / action完全禁用它(而不是扩大它),是否会有影响?整个申请?
  3. 如果没有,那么简单地忽略这些错误并从不记录它们是否安全?我是否与应用程序/服务器的恶意交互造成了盲点?

2 个答案:

答案 0 :(得分:1)

我注意到了同样的问题。我有更大的文件,用户可以从我的网站下载,有些人,根据地区,下载速度慢,下载停止一段时间后。我也在日志中看到例外。

我不建议禁用该功能,因为一些卡住或非常慢的连接,可能总结。只需增加时间并将速率降低到可以说您的网页仍然可用的值。

对于特定的网址,我不这样做。除非您注意到某些性能问题,否则页面具有相同设置时不会受到影响。

另请注意,还有MinResponseDataRate选项。

最好的方法是在应用程序启动时为所有路由设置一些合理的值。日志中总会出现例外情况,因为人们失去了与互联网的连接,而且需要在一段时间后关闭连接才能释放资源。

       public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseKestrel(options =>
            {
                options.Limits.MinRequestBodyDataRate =
                    new MinDataRate(bytesPerSecond: 80, gracePeriod: TimeSpan.FromSeconds(20));
                options.Limits.MinResponseDataRate =
                    new MinDataRate(bytesPerSecond: 80, gracePeriod: TimeSpan.FromSeconds(20));
            })
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.ClearProviders();
                logging.SetMinimumLevel(LogLevel.Trace);
            })
            .UseNLog()
            .UseStartup<Startup>()
            .Build();
}

请同时查看您的异步上传方法。确保您有等待或返回任务。我不认为它以这种方式编译。

答案 1 :(得分:0)

这是您可以使用在模型绑定之前运行的ResourceFilter进行的选择。

using System;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.Extensions.DependencyInjection;

namespace YourNameSpace
{
    public class RateFilter : Attribute, IResourceFilter
    {
        private const string EndPoint = "YourEndPoint";

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            try
            {
                if (!context.HttpContext.Request.Path.Value.Contains(EndPoint))
                {
                    throw new Exception($"This filter is intended to be used only on a specific end point '{EndPoint}' while it's being called from '{context.HttpContext.Request.Path.Value}'");
                }

                var minRequestRateFeature = context.HttpContext.Features.Get<IHttpMinRequestBodyDataRateFeature>();
                var minResponseRateFeature = context.HttpContext.Features.Get<IHttpMinResponseDataRateFeature>();
                //Default Bytes/s = 240, Default TimeOut = 5s

                if (minRequestRateFeature != null)
                {
                    minRequestRateFeature.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
                }

                if (minResponseRateFeature != null)
                {
                    minResponseRateFeature.MinDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
                }
            }
            catch (Exception ex)
            {
                //Log or Throw
            }
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    }
}

然后您可以在特定端点上使用属性,例如

        [RateFilter]
        [HttpPost]
        public IActionResult YourEndPoint(YourModel request)
        {
            return Ok();
        }
  • 您可以进一步自定义过滤器,以将端点/速率作为 ctor参数。
  • 您还可以删除对特定端点的检查
  • 您可以return代替throw