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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

子数整数(luogu 1151)

子数整数 luogu 1151 题目大意 给出一个数k&#xff0c;让你在10000~30000中求出满足前三位&#xff0c;中间三位&#xff0c;后三位都可被k整除的数 输入样例 15输出样例 22555 25555 28555 30000数据范围 0<k<1000 解题思路 暴力枚举 代码 #include<cstd…

2021-10-22

扫描线&#xff1a; https://www.cnblogs.com/Parsnip/p/10887135.html https://blog.csdn.net/Emma2oo6/article/details/120584307 https://blog.csdn.net/weixin_30609331/article/details/96234492 LIS& LCS https://www.xuebuyuan.com/586419.html https://blog.csdn…

.net core实践系列之短信服务-架构优化

前言通过前面的几篇文章&#xff0c;讲解了一个短信服务的架构设计与实现。然而初始方案并非100%完美的&#xff0c;我们仍可以对该架构做一些优化与调整。同时我也希望通过这篇文章与大家分享一下&#xff0c;我的架构设计理念。源码地址&#xff1a;https://github.com/SkyCh…

Poj 1061 青蛙的约会

Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 143491 Accepted: 33018 Description 文章目录题目&#xff1a;思路:代码:题目传送题目&#xff1a; 两只青蛙在网上相识了&#xff0c;它们聊得很开心&#xff0c;于是觉得很有必要见一面。它们很高兴地发现它们住…

P3649-[APIO2014]回文串【PAM】

正题 题目链接:https://www.luogu.com.cn/problem/P3649 题目大意 一个字符串&#xff0c;求最大的回文串长度出现次数 解题思路 构建出PAM\text{PAM}PAM然后统计一下每个节点作为后缀的次数&#xff0c;failfailfail树上上传一下信息就好了&#xff0c;时间复杂度O(n)O(n)O…

【数学】拉格朗日插值(luogu 4781/金牌导航 拉格朗日插值-1)

拉格朗日插值 luogu 4781 金牌导航 拉格朗日插值-1 题目大意 给出n个点&#xff0c;让你确定经过这n个点且不超过n-1次的方程&#xff0c;现在给出k&#xff0c;让你求出其函数值 样例#1 输入样例#1 3 100 1 4 2 9 3 16输出样例#1 10201样例#2 输入样例#2 3 100 1 1 …

游戏 (博弈论)

https://blog.csdn.net/Mys_C_K/article/details/91443390

.NET Core中Object Pool的简单使用

前言复用&#xff0c;是一个重要的话题&#xff0c;也是我们日常开发中经常遇到的&#xff0c;不可避免的问题。举个最为简单&#xff0c;大家最为熟悉的例子&#xff0c;数据库连接池&#xff0c;就是复用数据库连接。那么复用的意义在那里呢&#xff1f;简单来说就是减少不必…

数论 欧几里得与扩展欧几里得

欧几里得算法&#xff1a; 求a&#xff0c;b的最大公约数 gcd&#xff08;a,b&#xff09; gcd(b,a%b) ll gcd(ll a,ll b) {return b0?a:gcd(b,a%b); }扩展欧几里得算法&#xff1a; 如果a&#xff0c;b是整数&#xff0c;一定存在x和y使得axbygcd&#xff08;a&#xff0c…

P4173-残缺的字符串【FFT】

正题 题目链接:https://www.luogu.com.cn/problem/P4173 题目大意 给出两个字符串S,TS,TS,T&#xff0c;其中包含小写字母和一些???&#xff0c;???可以匹配任何字符。 求有多少个ppp使得T0∼∣t∣−1Sp∼p∣t∣−1T_{0\sim |t|-1}S_{p\sim p|t|-1}T0∼∣t∣−1​Sp∼…

转圈游戏(luogu 1965)

转圈游戏 luogu 1965 题目大意 求(xm10k)%n(x m\times 10^k)\%n(xm10k)%n 输入样例 10 3 45输出样例 5数据范围 1<n<10000000<m<n,1⩽x⩽n,0<k<1091<n<10000000<m<n,1\leqslant x\leqslant n,0<k<10^91<n<10000000<m<n…

.Net Core应用框架Util介绍(三)

上篇.Net Core应用框架Util介绍&#xff08;二&#xff09;介绍了Util的开发环境&#xff0c;并让你把Demo运行起来。本文将介绍该Demo的前端Angular运行机制以及目录结构。目录结构在VS上打开Util Demo&#xff0c;会看见如下的目录结构。现代前端通常采用VS Code开发&#xf…

【DP】字串距离(luogu 1279)

字串距离 luogu 1279 题目大意 给出两个字符串&#xff0c;让你加上若干空格&#xff0c;使其长度相同 对于第i位&#xff0c;如果都是字母那代价就是ASCII码的差值&#xff0c;如果一个是字母那就是k&#xff0c;如果没有一个是字母那就是0&#xff0c;让你求最小代价 输入…

hdu 1525 Euclid‘s Game

hdu 1525 文章目录Problem Description题意&#xff1a;题解&#xff1a;代码&#xff1a;Problem Description Two players, Stan and Ollie, play, starting with two natural numbers. Stan, the first player, subtracts any positive multiple of the lesser of the two n…

P4351-[CERC2015]Frightful Formula【组合数学,MTT】

正题 题目链接:https://www.luogu.com.cn/problem/P4351 题目大意 n∗nn*nn∗n的矩形&#xff0c;给出第一行和第一列的数&#xff0c;剩下的满足Fi,ja∗Fi,j−1b∗Fi−1,jcF_{i,j}a*F_{i,j-1}b*F_{i-1,j}cFi,j​a∗Fi,j−1​b∗Fi−1,j​c 求Fn,nF_{n,n}Fn,n​ 解题思路 第…

阶乘幂与有限微积分

定义 差分算子Δ\DeltaΔ&#xff1a;Δf(x)f(x1)−f(x)\Delta f(x)f(x1)-f(x)Δf(x)f(x1)−f(x)平移算子EEE&#xff1a;Ef(x)f(x1)E f(x)f(x1)Ef(x)f(x1)下降幂&#xff1a;n>0,{xn‾x(x−1)(x−2)...(x−n1)x−n‾1(x1)(x2)(x3)...(xn)n>0,\begin{cases}x^{\underlin…

【Floyd】灾后重建(luogu 1119)

灾后重建 luogu 1119 题目大意 给你一个图&#xff0c;每个点在一定时间后才能被通过&#xff0c;问在某个时间&#xff0c;两个点之间的最短路 输入样例 4 5 1 2 3 4 0 2 1 2 3 1 3 1 2 2 1 4 0 3 5 4 2 0 2 0 1 2 0 1 3 0 1 4输出样例 -1 -1 5 4数据范围 N⩽200&#x…

Ocelot简易教程(三)之主要特性及路由详解

作者&#xff1a;依乐祝原文地址&#xff1a;https://www.cnblogs.com/yilezhu/p/9664977.html上篇《Ocelot简易教程&#xff08;二&#xff09;之快速开始2》教大家如何快速跑起来一个ocelot实例项目&#xff0c;也只是简单的对Ocelot进行了配置&#xff0c;这篇文章会给大家详…

P4245-[模板]任意模数多项式乘法

正题 题目链接:https://www.luogu.com.cn/problem/P4245 题目大意 两个多项式&#xff0c;求它们的乘积模ppp。 解题思路 方法好像挺多&#xff0c;我用的是最简单的一种就是&#xff0c;先定一个常数sqqsqqsqq&#xff08;一般是q\sqrt qq​&#xff09;&#xff0c;把一个…

Poj 3070 Fibonacci

Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 26063 Accepted: 17394文章目录Description题意&#xff1a;题解&#xff1a;代码&#xff1a;Poj 3070 FibonacciDescription In the Fibonacci integer sequence, F0 0, F1 1, and Fn Fn − 1 Fn − 2 for…