asp.net-core - 覆盖默认的 asp.net 核心取消令牌或更改请求的默认超时
问题描述
在我的 asp.net core 5.0 应用程序中,我调用了一个可能需要一些时间来处理的异步方法。
await someObject.LongRunningProcess(cancelationToken);
但是,我希望该方法在 5 秒后超时。我知道,我可以使用“CancellationTokenSource”,而不是 asp.net 核心操作传递的“cancelationToken”:
var s_cts = new CancellationTokenSource();
s_cts.CancelAfter(TimeSpan.FromSeconds(5);
await someObject.LongRunningProcess(s_cts );
是否可以将“ CancellationTokenSource ”用作所有 asp.net 核心请求的默认“取消令牌”策略?我的意思是覆盖作为动作参数传递的那个?
或者是否可以更改 asp.net core 5.0 中所有请求的默认超时?
[更新]
解决方案
解决这个问题的一种方法是将该逻辑包装在一个类中。编写一个运行具有可配置超时的任务的类。
然后在 DI 中注册它,然后在任何你想重用配置的地方使用它。
public class TimeoutRunner
{
private TimeoutRunnerOptions _options;
public TimeoutRunner(IOptions<TimeoutRunnerOptions> options)
{
_options = options.Value;
}
public async Task<T> RunAsync<T>(Func<CancellationToken, Task<T>> runnable,
CancellationToken cancellationToken = default)
{
// cancel the task as soon as one of the tokens is set
var timeoutCts = new CancellationTokenSource();
var token = timeoutCts.Token;
if (cancellationToken != default)
{
timeoutCts.CancelAfter(_options.Timeout);
var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
token = combinedCts.Token;
}
return await runnable(token);
}
}
internal static class ServiceCollectionExtensions
{
public static IServiceCollection AddTimeoutRunner(this IServiceCollection services,
Action<TimeoutRunnerOptions> configure = null)
{
if (configure != null)
{
services.Configure<TimeoutRunnerOptions>(configure);
}
return services.AddTransient<TimeoutRunner>();
}
}
public class TimeoutRunnerOptions
{
public int TimeoutSeconds { get; set; } = 10;
public TimeSpan Timeout => TimeSpan.FromSeconds(TimeoutSeconds);
}
然后你会在 Startup 类中注册它,
public void ConfigureServices(IServiceCollection services)
{
services.AddTimeoutRunner(options =>
{
options.TimeoutSeconds = 10;
});
}
然后在需要该全局选项的任何地方使用它:
public class MyController : ControllerBase
{
private TimeoutRunner _timeoutRunner;
public MyController(TimeoutRunner timeoutRunner)
{
_timeoutRunner = timeoutRunner;
}
public async Task<IActionResult> DoSomething(CancellationToken cancellationToken)
{
await _timeoutRunner.RunAsync(
async (CancellationToken token) => {
await Task.Delay(TimeSpan.FromSeconds(20), token);
},
cancellationToken
);
return Ok();
}
}
在每次动作调度之前运行一个任务
方法 1:动作过滤器
我们可以使用操作过滤器在每个请求之前/之后运行任务。
public class ApiCallWithTimeeotActionFilter : IAsyncActionFilter
{
private TimeoutRunner _runner;
public ApiCallWithTimeeotActionFilter(TimeoutRunner runner)
{
_runner = runner;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var result = await _runner.RunAsync(
async (CancellationToken token) =>
{
await Task.Delay(TimeSpan.FromSeconds(20), token);
return 42;
},
default
);
await next();
}
}
然后使用它注释一个类[TypeFilter(typeof(MyAction))]
:
[TypeFilter(typeof(ApiCallWithTimeeotActionFilter))]
public class MyController : ControllerBase { /* ... */ }
方法二:中间件
另一种选择是使用中间件
class ApiCallTimeoutMiddleware
{
private TimeoutRunner _runner;
public ApiCallTimeoutMiddleware(TimeoutRunner runner)
{
_runner = runner;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// run a task before every request
var result = await _runner.RunAsync(
async (CancellationToken token) =>
{
await Task.Delay(TimeSpan.FromSeconds(20), token);
return 42;
},
default
);
await next(context);
}
}
然后在方法中附加中间件Startup.Configure
:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<ApiCallTimeoutMiddleware>();
app.UseRouting();
app.UseEndpoints(e => e.MapControllers());
}
推荐阅读
- python - hashlib 中的 Sha 256 函数返回 NoneType
- coldfusion - 如何在 ColdFusion 中保持选择下拉选项
- c# - 在编译 Xamarin.Forms 项目时修复“VerifyVersionsTask”错误
- pdflib - PDFlib - 控制文本颜色、文本背景和文本笔划的背景和不透明度
- asp.net-mvc-5 - ELMAH_Error 表中记录的错误未显示在 elmah.axd 页面上
- php - 在 Windows 中使用 Docker 时的 Wordpress 和 DB 文件
- typescript - 如何将 RethinkDB 集成到 Angular 7 Web 应用程序中?
- hive - 使用特殊字符插入 Hive 表内容 - 制表符空格和换行符
- kubernetes-helm - Helm 未显示版本/无法删除旧版本
- node.js - Express JS 在不关闭服务器的情况下清除内存的方法?