ASP.NET Core 中断请求了解一下(翻译)

2023-11-06 14:58

本文主要是介绍ASP.NET Core 中断请求了解一下(翻译),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文所讲方式仅适用于托管在Kestrel Server中的应用。如果托管在IIS和IIS Express上时,ASP.NET Core Module(ANCM)并不会告诉ASP.NET Core在客户端断开连接时中止请求。但可喜的是,ANCM预计在.NET Core 2.2中会完善这一机制。

1. 引言

假设有一个耗时的Action,在浏览器发出请求返回响应之前,如果刷新了页面,对于浏览器(客户端)来说前一个请求就会被终止。而对于服务端来说,又是怎样呢?前一个请求也会自动终止,还是会继续运行呢?

下面我们通过实例寻求答案。

2. 实例演示

创建一个SlowRequestController,再定义一个Get请求,并通过Task.Delay(10_000)模拟耗时行为。代码如下:

640?wx_fmt=png

如果我们发起请求,那么该页面将耗时10s才能完成显示。
640?wx_fmt=png
如果我们检查运行日志,我们发现其输出符合预期:
640?wx_fmt=png

如果在第一次请求返回之前,刷新页面,结果将是怎样呢??

640?wx_fmt=png
从日志中我们可以看出:刷新后,第一个请求虽然在客户端被取消了,但是服务端仍旧会持续运行。

从而可以说明MVC的默认行为: 即使用户刷新了浏览器会取消原始请求,但MVC对其一无所知,已经被取消的请求还是会在服务端继续运行,而最终的运行结果将会被丢弃。

这样就会造成严重的性能浪费。如果服务端能感知用户中断了请求,并终止运行耗时的任务就好了。

幸好,ASP.NET Core开发团队体贴的考虑了这一点,允许我们通过以下两种方式来获取客户端的请求是否被终止。

  1. 通过HttpContexRequestAborted属性:

  2. 通过方法注入CancellationToken参数:

640?wx_fmt=png

而这两种方式其实是一样的,因为HttpContext.RequestAbortedcancellationToken对应的是同一个对象:

if(cancellationToken == HttpContext.RequestAborted)
{    // this is true!
}

下面我们就来以cancellationToken为例,看看如何感知客户端请求终止并终止服务端服务。

3. 在Action中使用CancellationToken

CancellationToken是由CancellationTokenSource创建的轻量级对象。当某个CancellationTokenSource被取消时,它会通知所有的消费者CancellationToken

取消时,CancellationTokenIsCancellationRequested属性将设置为True,表示CancellationTokenSource已取消。

再回到前面的实例,我们有一个长期运行的操作方法(例如,通过调用许多其他API生成只读报告)。由于它是一种昂贵的方法,我们希望在用户取消请求时尽快停止执行操作。

下面的代码显示了通过在action方法中注入一个CancellationToken,并将其传递给Task.Delay,来达到同步终止服务端请求的目的:

640?wx_fmt=png

MVC将使用CancellationTokenModelBinder自动将Action中的任何CancellationToken参数绑定到HttpContext.RequestAborted。当我们在Startup.ConfigureServices()中调用services.AddMvc() 或 services.AddMvcCore()时,CancellationTokenModelBinder模型绑定器就会被自动注册。

通过这个小改动,我们再尝试在第一个请求返回之前刷新页面,从日志中我们发现,第一个请求将不会继续完成。而是当Task.Delay检测到CancellationToken.IsCancellationRequested属性为true时立即停止执行时并抛出TaskCancelledException
640?wx_fmt=png

简而言之,用户刷新浏览器,在服务端通过抛出TaskCancelledException异常终止了第一个请求,而该异常通过请求管道再传播回来。

在这个场景中,Task.Delay()会监视CancellationToken,因此无需我们手动检查CancellationToken是否被取消。

4. 手动检查CancellationToken状态

如果你正在调用支持CancellationToken的内置方法,比如Task.Delay()HttpClient.SendAsync(),那么你可以直接传入CancellationToken,并让内部方法负责实际取消。
在其他情况下,您可能正在进行一些同步工作,您希望能够取消这些工作。例如,假设正在构建一份报告来计算公司员工的所有佣金。你循环每个员工,然后遍历他们的每一笔销售。

能够在中途取消此报告生成的简单解决方案是检查for循环内的CancellationToken,如果用户取消请求则跳出循环。
以下示例通过循环10次并执行某些同步(不可取消)工作来表示此类情况,该工作由对Thread.Sleep()来模拟。在每个循环开始时,我们检查CancellationToken,如果取消则抛出异常。这使得我们可以终止一个长时间运行的同步任务。

640?wx_fmt=png

现在,如果你取消请求,则对ThrowIfCancelletionRequested()的调用将抛出一个OperationCanceledException,它将再次传播回过滤器管道和中间件管道。

5. 使用ExceptionFilter捕捉取消异常

ExceptionFilters是一个MVC概念,可用于处理在您的操作方法或操作过滤器中发生的异常。可以参考官方文档。

可以将过滤器应用到控制器级别和操作级别,也可以应用于全局级别。为了简单起见,我们创建一个过滤器并添加到全局过滤器。

640?wx_fmt=png

我们通过重载OnException方法并特殊处理OperationCanceledException异常即可成功捕获取消异常。

Task.Delay()抛出的异常是TaskCancelledException类型,其为OperationCanceledException的基类,所以,以上过滤器也可正确捕捉。

然后注册过滤器:

public class Startup{    public void ConfigureServices(IServiceCollection services){services.AddMvc(options =>{options.Filters.Add<OperationCancelledExceptionFilter>();});}
}

现在再测试,我们发现运行日志将不会包含异常信息,取而代之的是我们自定义的信息。

640?wx_fmt=png

6. 最后

通过本文,我们知道用户可以通过点击浏览器上的停止或重新加载按钮随时取消Web应用的请求。而实际上仅仅是终止了客户端的请求,服务端的请求还在继续运行。对于简单耗时短的请求来说,我们可以不予理睬。但是,对于耗时任务来说,我们却不可以置若罔闻,因为其有很高的性能损耗。

而如何解决呢?其关键是通过CancellationToken来捕捉用户请求的状态,从而根据需要进行相应的处理。

参考资料:
CancellationTokens and Aborted ASP.NET Core Requests
Using CancellationTokens in ASP.NET Core MVC controllers


原文地址: https://www.cnblogs.com/sheng-jie/p/9660288.html


 

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

这篇关于ASP.NET Core 中断请求了解一下(翻译)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/357299

相关文章

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

在.NET平台使用C#为PDF添加各种类型的表单域的方法

《在.NET平台使用C#为PDF添加各种类型的表单域的方法》在日常办公系统开发中,涉及PDF处理相关的开发时,生成可填写的PDF表单是一种常见需求,与静态PDF不同,带有**表单域的文档支持用户直接在... 目录引言使用 PdfTextBoxField 添加文本输入域使用 PdfComboBoxField

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

一文带你了解SpringBoot中启动参数的各种用法

《一文带你了解SpringBoot中启动参数的各种用法》在使用SpringBoot开发应用时,我们通常需要根据不同的环境或特定需求调整启动参数,那么,SpringBoot提供了哪些方式来配置这些启动参... 目录一、启动参数的常见传递方式二、通过命令行参数传递启动参数三、使用 application.pro

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例

基于.NET编写工具类解决JSON乱码问题

《基于.NET编写工具类解决JSON乱码问题》在开发过程中,我们经常会遇到JSON数据处理的问题,尤其是在数据传输和解析过程中,很容易出现编码错误导致的乱码问题,下面我们就来编写一个.NET工具类来解... 目录问题背景核心原理工具类实现使用示例总结在开发过程中,我们经常会遇到jsON数据处理的问题,尤其是

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服