【问题标题】: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)可能有自己的超时,并且此超时与该超时完全分开。