You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

集成OWIN与Autofac的ASP.NET Web API 2请求取消后代码执行异常

OWIN+Autofac集成的ASP.NET Web API 2中,客户端取消请求后await后续代码无法执行的问题

问题重现

我正在开发一个基于OWIN和Autofac的ASP.NET Web API 2应用,遇到以下异常行为:

预期流程

  • 浏览器发起请求
  • 服务器启动长时间异步操作(await Task.Delay(10000))
  • 浏览器在5秒后中止请求
  • 服务器完成Task.Delay执行
  • ContinueWith回调及await之后的代码正常执行

实际流程

  • 浏览器发起请求
  • 服务器启动长时间异步操作
  • 浏览器在5秒后中止请求
  • 服务器立即返回500响应,未等待Task.Delay完成
  • 10秒后仅ContinueWith方法执行,await之后的代码完全不运行

该问题仅在同时集成OWIN与Autofac的Web API 2项目中出现,单独使用Web API 2、Web API 2+Autofac、Web API 2+OWIN的配置下均能得到预期结果。

相关代码

API控制器代码

[HttpGet]
[Route("test")]
public async Task<string> GetAll()
{
    await DoLongRunningOperationAsync(CancellationToken.None);

    return "Success";
}

private async Task DoLongRunningOperationAsync(CancellationToken cancellationToken)
{
    try
    {
        Debug.WriteLine("Starting delay");
        
        await Task.Delay(10000, cancellationToken)
            .ContinueWith(task =>
            {
                Debug.WriteLine("Continue With");
            });

        Debug.WriteLine("Delay completed successfully");
    }
    catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
    {
        Debug.WriteLine("Delay was canceled");
        throw;
    }
}

OWIN启动配置

public void Configuration(IAppBuilder app)
{
    var config = new HttpConfiguration();
    
    config.MapHttpAttributeRoutes();
    
    var builder = new ContainerBuilder();
    
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    builder.RegisterType<TestService>().As<ITestService>().InstancePerRequest();
    
    var container = builder.Build();
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    
    app.UseAutofacMiddleware(container);
    app.UseAutofacWebApi(config);
    app.UseWebApi(config);            
}

客户端测试代码

var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:59475/test/test");
xhttp.send();
setTimeout(() => xhttp.abort(), 5000)

原因分析

  1. 请求上下文与Autofac生命周期绑定:Autofac的InstancePerRequest会将服务生命周期绑定到OWIN请求上下文,当客户端取消请求时,OWIN会立即终止请求上下文,Autofac随之释放请求范围内的所有服务实例。此时await完成后尝试回到原请求上下文执行后续代码时,上下文已被销毁,导致代码无法继续执行。
  2. 上下文同步问题:未使用ConfigureAwait(false)时,await默认会尝试回到原请求上下文(包含Autofac请求容器),而该上下文在请求取消后已被清理,直接导致后续代码执行失败。
  3. ContinueWith的特殊性:ContinueWith默认在后台线程池执行,不依赖原请求上下文,因此即使请求上下文被销毁,仍能正常运行。

解决方案

方案1:解除异步代码与请求上下文的绑定

在await调用时添加ConfigureAwait(false),避免回到已被销毁的请求上下文:

await Task.Delay(10000, cancellationToken)
    .ContinueWith(task =>
    {
        Debug.WriteLine("Continue With");
    }).ConfigureAwait(false);

方案2:将长时间操作移至后台任务(不依赖请求上下文)

如果后续代码需要访问服务实例,不要使用请求范围内的服务,而是将逻辑移到独立于请求的后台任务中,例如使用Task.Run并确保不捕获请求上下文:

private async Task DoLongRunningOperationAsync(CancellationToken cancellationToken)
{
    try
    {
        Debug.WriteLine("Starting delay");
        
        // 用Task.Run将操作移至后台线程,脱离请求上下文
        await Task.Run(async () =>
        {
            await Task.Delay(10000, cancellationToken);
            Debug.WriteLine("Continue With");
        }, cancellationToken).ConfigureAwait(false);

        Debug.WriteLine("Delay completed successfully");
    }
    catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
    {
        Debug.WriteLine("Delay was canceled");
        throw;
    }
}

方案3:调整Autofac中间件行为(不推荐)

不建议修改Autofac默认的请求生命周期管理,但如果必须在请求取消后保留容器,可以自定义Autofac的OWIN中间件,延迟请求容器的释放。不过这种做法可能导致资源泄漏,需谨慎使用。

验证步骤

  1. 应用上述方案之一修改代码
  2. 启动Web API服务,用客户端代码发起请求并在5秒后取消
  3. 查看Debug输出,确认Delay completed successfully日志正常打印
  4. 检查服务器响应,确认即使客户端取消,异步逻辑仍能完整执行

内容的提问来源于stack exchange,提问作者user25652029

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:53:10