如何使用 Angular 取消 .Net Core Web API 请求?

Posted

技术标签:

【中文标题】如何使用 Angular 取消 .Net Core Web API 请求?【英文标题】:How to cancel .Net Core Web API request using Angular? 【发布时间】:2020-01-04 20:42:03 【问题描述】:

我有以下两个应用程序

Angular 6/7 应用程序 .Net Core Web API

我正在使用 Angular 的 HttpClient 向 API 发出 GET 请求,如下所示

this.subscription = this.httpClient.get('api/Controller/LongRunningProcess')
                                   .subscribe((response) => 
                                   
                                      // Handling response
                                   );

API 控制器的 LongRunningProcess 方法有如下代码

    [HttpGet]
    [Route("LongRunningProcess")]
    public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
    
        try
        
            // Dummy long operation
            await Task.Factory.StartNew(() =>
            
                for (int i = 0; i < 10; i++)
                
                    // Option 1 (Not working)
                    if (cancellationToken.IsCancellationRequested)
                        break;

                    // Option 2 (Not working)
                    cancellationToken.ThrowIfCancellationRequested();

                    Thread.Sleep(6000);
                

            , cancellationToken);
        
        catch (OperationCanceledException e)
        
            Console.WriteLine($"nameof(OperationCanceledException) thrown with message: e.Message");
        

        return Ok();
    

现在我想取消这个长时间运行的过程,所以我从客户端取消订阅,如下所示

// On cancel button's click
this.subscription.unsubscribe();

上面的代码将cancel请求,我可以在浏览器的Network标签中看到它被取消了,如下所示

但它不会在API的LongRunningProcess方法中使IsCancellationRequested变为true,所以操作会继续进行。

[注意]: API 方法中的 Option 1Option 2 都不起作用,即使我使用 postman 拨打电话。

问题:有没有办法取消那个LongRunningProcess方法的操作?

【问题讨论】:

在同时使用 Kestrel 和 IIS Express 时可能会出现问题。我尝试不使用 IIS Express(请参阅 ***.com/questions/39574061/… 和 ***.com/questions/47153525/…),它按预期完美运行。我得到 IsCancellationRequested = true。 这个问题有答案吗?我有同样的问题。 我稍后没有检查,但如果您阅读我之前的评论可能会有所帮助 【参考方案1】:

当角度取消请求时,您可以从 http 上下文中获取取消令牌

   CancellationToken cancellationToken = HttpContext.RequestAborted;
    if (cancellationToken.IsCancellationRequested)
    
        // The client has aborted the request
    

【讨论】:

【参考方案2】:

在这种情况下你不需要休息,只需要这样使用

 [HttpGet]
 [Route("LongRunningProcess")]
 public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
 
   for (int i = 0; i < 10; i++)
   
      cancellationToken.ThrowIfCancellationRequested();
       // Dummy long operation
       await Task.Factory.StartNew(() => Thread.Sleep(60000));
   

    return Ok();
 

您可以阅读更多here

【讨论】:

感谢您的回复。我已经浏览了上面的博客,它还指出“当检测到 CancellationToken.IsCancellationRequested 属性为 true 时,Task.Delay 调用会引发 TaskCancelledException,立即停止执行。”甚至微软文档都说“ThrowIfCancellationRequested”方法提供了等效于 if (token.IsCancellationRequested)throw new OperationCanceledException(token); 的功能。但问题是取消令牌无法检测到请求被取消。 我会再试一次,并会就此与您联系。【参考方案3】:

这是因为您的 dummy long 操作不监控 canncellationToken。我不确定您是否真的打算毫无延迟地并行启动 10 个一分钟的任务,这就是您的代码所做的。

为了进行虚拟长操作,代码如下

[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)

    // Dummy long operation
    await Task.Run(() =>
        
            for (var i = 0; i < 60; i++)
            
                if (cancel.IsCancellationRequested)
                    break;
                Task.Delay(1000).Wait();
            
        );

    return Ok();

顺便说一句,Task.Run 就相当于 Task.Factory.StartNew

但是,如果您只需要在 Web API 中进行一个虚拟的长期运行操作,那么您也可以简单地使用支持取消令牌的 Task.DelayTask.Delay在请求被取消的时候会抛出异常,所以在请求取消后需要做一些事情的时候添加异常处理代码。

[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)

    // Dummy long operation
    await Task.Delay(60000, cancel);

    return Ok();

【讨论】:

感谢您的回复。在这个问题中,我没有编写代码来在任何任务中传递取消令牌。但在实际代码中,我在循环中放置了一个断点,然后取消了来自客户端的请求并进行了检查。它没有使“IsCancellationRequested”变为“真”。不过,我会再试一次并回复你。 我明白了,那么问题一定出在您调用 this.subscription.unsubscribe() 的 Angular 代码中。将围绕它的代码附加到您的问题中怎么样? 我已经在问题中写了一段代码。据我所知,取消任何请求只需要那么多代码。 因为我在您提供的客户端代码中没有发现任何错误,所以我只是怀疑您在其余客户端代码中做了什么。会有问题的。不过也有可能 顺便说一下,Task.Run 就相当于 Task.Factory.StartNew。。 almost【参考方案4】:

除非您在 onDestroy() 中取消订阅,否则当时仍在运行的任何 http observable 都将完成并运行它们的逻辑。结果是否微不足道取决于您在订阅处理程序中所做的事情。如果您尝试更新不再存在的内容,您可能会收到错误消息。

提示:订阅包含一个封闭的布尔属性,在高级情况下可能很有用。对于 HTTP,这将在完成时设置。在 Angular 中,在某些情况下,在 ngDestroy 中设置 _isDestroyed 属性可能很有用,您的订阅处理程序可以检查该属性。

提示 2:如果处理多个订阅,您可以创建一个临时的 new Subscription() 对象并向其添加(...)任何其他订阅 - 因此,当您取消订阅主要订阅时,它将取消订阅所有添加的订阅也是。

因此,最佳实践是使用 takeUntil() 并在组件被销毁时取消订阅 http 调用。

   import  takeUntil  from 'rxjs/operators';
   .....
   ngOnDestroy(): void 
     this.destroy$.next();  // trigger the unsubscribe
     this.destroy$.complete(); // finalize & clean up the subject stream
   

【讨论】:

通常我用下面的方式(和你说的一样) import Subject from 'rxjs';从 'rxjs/operators' 导入 takeUntil ;私人退订者 = 新主题(); ngOnDestroy() if (this.unsubscriber) this.unsubscriber.next(); this.unsubscriber.complete();但是在上述情况下(在一个问题中)我想取消按钮上的请求,所以我订阅并取消订阅按钮的点击。【参考方案5】:
var cancellationToken = new CanellationToken();
    cancellationToken.CancelAfter(2000);
    using (var response = await _httpClient.GetAsync("emp", 
        HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
    
        response.EnsureSuccessStatusCode();
        var stream = await response.Content.ReadAsStreamAsync();
        var emp = await JsonSerializer.DeserializeAsync<List<empDto>>(stream, _options);
    

此外,我们还可以有这个“CancellationToken”类,它只是一个在一定时间间隔后终止请求的 Http 客户端方法。

【讨论】:

以上是关于如何使用 Angular 取消 .Net Core Web API 请求?的主要内容,如果未能解决你的问题,请参考以下文章

Angular + core 如何将文件数组从 Angular 上传到 Dot Net Core

如何使用 .NET Core 2 配置 Angular 6 以允许来自任何主机的 CORS?

asp.net core + angular2 JWT 承载

如何从.net core 2.1 Angular 6模板中的非相对路径导入ts和scss文件

如何将数据表从 .net core 5.0 web api 作为 json 传递到 Angular 前端?

使用 Postman 登录 Angular ASP.NET CORE 应用程序