【问题标题】:Controller timeout on dotnet coredotnet 核心上的控制器超时
【发布时间】:2022-10-13 00:34:33
【问题描述】:

我在 dotnet core 3.1 上有一个 web api,我想设置不同的超时特定控制器操作。我尝试创建一个类似下面的 actionfilter

public class TimeOutAttribute : ActionFilterAttribute
{
    private readonly int _timeout;

    public TimeOutAttribute(int timeout)
    {
        _timeout = timeout;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        try
        {
            var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(_timeout));
            await Task.Run(async () => await next(), cts.Token);
        }
        catch (TaskCanceledException)
        {
            var request = context.HttpContext.Request;
            var message = $"Action exceeded the set timeout limit {_timeout} milisecond for {request.PathBase}{request.Path}";
            throw new ActionTimeOutException(message);
        }
    }
}

我在控制器方法上使用它

[TimeOut(100)]
public async Task<IActionResult> Get()
{
}

虽然 Get 方法花费了 100 多毫秒,但我无法得到异常。你能在代码上看到任何问题吗,或者如果你有另一个控制器超时选项我准备试试

【问题讨论】:

    标签: c# .net-core controller timeout action-filter


    【解决方案1】:

    你能在代码上看到任何问题吗

    是的;将取消令牌传递给Task.Run 是行不通的。 token for that method 只取消调度任务到线程池,而不是委托本身。

    取消您的委托代码的唯一方法是让您的委托接受CancellationToken 并观察它(通常通过将其传递给其他方法)。我有一个关于取消的blog post series

    如果您有其他控制器超时选项我准备尝试

    所以,这是一个更难的问题。

    ASP.NET Core 内置了对CancellationToken 的支持;您可以将CancellationToken 参数添加到任何控制器操作方法。但是,这个令牌与超时没有任何关系;如果用户请求被中止(例如,用户关闭浏览器),它会取消。

    一种方法是将CancellationToken 参数添加到您的操作方法并让您的过滤器修改模型绑定结果,替换提供的CancellationToken。像这样的东西应该工作:

    public sealed class TimeoutAttribute : ActionFilterAttribute
    {
        private readonly TimeSpan _timeout;
        public TimeoutAttribute(int timeoutMilliseconds) => _timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
    
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // Find the CancellationToken argument passed to the action
            var cancellationTokenArgument = context.ActionArguments.FirstOrDefault(x => x.Value is CancellationToken);
            if (cancellationTokenArgument.Key == null || cancellationTokenArgument.Value == null)
                throw new InvalidOperationException("TimeoutAttribute must be used on an action with a CancellationToken");
    
            // Create a new CancellationToken that will be cancelled if *either* the user disconnects *or* a timeout
            using var cts = CancellationTokenSource.CreateLinkedTokenSource((CancellationToken)cancellationTokenArgument.Value);
            cts.CancelAfter(_timeout);
    
            // Replace the action's CancellationToken argument with our own
            context.ActionArguments[cancellationTokenArgument.Key] = cts.Token;
    
            await next();
        }
    }
    

    这将在一定程度上起作用。您的主机(即 IIS)可能有自己的超时,并且此超时与该超时完全分开。

    【讨论】:

      猜你喜欢
      • 2017-12-07
      • 2018-08-19
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      • 2016-09-25
      • 2020-05-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多