【ASP.NET Core】绑定到 CancellationToken 对象
负责管理 HTTP 请求上下文的 HttpContext 对象有一个名为 RequestAborted 的属性。据其名思其义,就是可用来表示客户端请求是否已取消。
果然,它的类型是 CancellationToken,这家伙是结构类型,为啥强调是结构呢——因为是值类型啊。在访问 HTTP 的整个上下文传递过程,直接赋值会复制多个实例,弄不好就会搞得一个请求通信期间状态数据不一致。所以,类库内部在传递此属性值时会用 object 类型的变量来引用它的值,嗯,对的,就是“装箱”。以引用类型的方式操作它,可以避免对象的复制而造成数据不统一。
具体可以看看 CancellationTokenModelBinder 类的源代码(命名空间:Microsoft.AspNetCore.Mvc.ModelBinding.Binders)。
public class CancellationTokenModelBinder : IModelBinder
{
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
// We need to force boxing now, so we can insert the same reference to the boxed CancellationToken
// in both the ValidationState and ModelBindingResult.
//
// DO NOT simplify this code by removing the cast.
var model = (object)bindingContext.HttpContext.RequestAborted;
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
以上内容,大伙伴们应该能看懂的。看不懂咋办?没事的,不要自卑,不必跳河,看不懂但知道怎么用就行。
重点看这一句:
var model = (object)bindingContext.HttpContext.RequestAborted;
把它强制转换为 object 类型再赋值,确保赋值后 CancellationToken 实例没有被复制。
如果在提交 HTTP 后,以及在服务器处理完毕返回消息给客户端之前,如果客户端关闭(取消)了连接(比如,关掉浏览器,单击“取消”请求,网络断了,路由器着火了等情况),那么,透过 HttpContext.RequestAborted 属性我们在服务器代码中就获得相关信息。说直接一点,就是 IsCancellationRequested 会返回 true。
老周暂不讲模型绑定的事,先看看这个 RequestAborted 属性如何使用。
来个示例。从前,有个 controller 名叫 Happy,它有两个儿子(action),老大叫 Index,老二叫 ChouJiang(抽奖)。Happy 家里开了个彩票店,老大 Index 负责门面,喜迎南北客;老二负责业务,包括把开奖结果告诉客人。
public class HappyController : Controller
{
// 用来随机生成幸运数字
private static readonly Random rand = new((int)DateTime.Now.ToBinary());
// 记录日志,可通过依赖注入解决实例化问题
private readonly ILogger logger;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="logfac">从依赖注入获取</param>
public HappyController(ILoggerFactory logfac)
{
logger = logfac.CreateLogger("Demo Log");
}
public IActionResult Index()
{
// 门面功夫,开门迎客
return View("~/views/TestView1.cshtml");
}
public async Task<IActionResult> ChouJiang()
{
// 抽奖模拟中
int x = 5;
int result = 0;
// 抽五次,选最后一个幸运数字
while(x > 0)
{
// 如果连接挂了,直接拜拜
if(HttpContext.RequestAborted.IsCancellationRequested)
{
logger.LogInformation("请求已取消");
return NoContent();
}
await Task.Delay(500); //模拟延时
x--;
result = rand.Next(0, 1000);//生成随机数
}
// 开大奖了
return Content($"<script>alert('幸运数字:{result}')</script>", "text/html", Encoding.UTF8);
}
}
此例中的核心是判断 HttpContext.RequestAborted.IsCancellationRequested 是否为 true。如果是,那么这一轮抽奖活动结束。
下面 Razor 代码是 Happy 彩票店的门面装修效果,请隔壁老王设计的。
@{
ViewBag.Title = "演示-1";
}
<p>点击下面链接,开启虎年幸运大奖</p>
<a target="_blank" asp-action="ChouJiang" asp-controller="Happy">抽奖</a>
把示例运行起来。
点击页面上的链接,如果你有足够的耐心,等其完成抽奖,会看到幸运数字。
如果你觉得没意思,在点击链接后,点击浏览器上的“X”,取消操作,会看到日志输出,表示连接断了/请求取消了。
动不动就去访问 HttpContext.RequestAborted.IsCancellationRequested 也不怎么方便,至少没有方便面方便。所以,咱们要做一进升级——使用模型绑定。
要求是:
- 绑定的对象类型是 CancellationToken
- 绑定目标可以是 action 方法参数,也可以是 Controller 的属性(MVC),或 Model Page 的属性(Razor Pages)。
于是,上面的抽奖代码可以这样改:
public async Task<IActionResult> ChouJiang(CancellationToken ct)
{
// ……
while(x > 0)
{
// 如果连接挂了,直接拜拜
if(ct.IsCancellationRequested)
{
logger.LogInformation("请求已取消");
return NoContent();
}
await Task.Delay(500); //模拟延时
x--;
result = rand.Next(0, 1000);//生成随机数
}
// ……
}
也可以在 Controller 中定义属性来绑定。把本例进行修改。
// 这是属性
[BindProperty(SupportsGet = true)]
public CancellationToken CancelTK { get; set; }
public async Task<IActionResult> ChouJiang()
{
// ……
while(x > 0)
{
// 如果连接挂了,直接拜拜
if(CancelTK.IsCancellationRequested)
{
//……
}
await Task.Delay(500); //模拟延时
x--;
result = rand.Next(0, 1000);//生成随机数
}
// ……
}
如果用属性来绑定,那么在属性上应用 BindProperty 特性是必须的。这里要把 SupportsGet 设置为 true,因为老周这个例子中,视图是点击链接后调用抽奖代码的,是以 HTTP-GET 方式请求的,而默认情况是 BindProperty 在 GET 方式时不进行绑定。所以,为了能顺利绑定,就得把 SupportsGet 改为 true;如果你用的是 POST 方式触发,就不用设置。
【ASP.NET Core】绑定到 CancellationToken 对象的更多相关文章
- 如何为ASP.NET Core的强类型配置对象添加验证
原文: Adding validation to strongly typed configuration objects in ASP.NET Core 作者: Andrew Lock 译文: La ...
- Asp.Net Core获取当前上下文对象
HttpContext简介 .Net Core中的HttpContext上下文是个抽象类,命名空间为Microsoft.AspNetCore.Http 所在程序集 \netstandard2.0\Mi ...
- asp.net core过滤器记录响应对象
百度到的基本上就是读取response.body的流.然后记录完了之后,把流的index重新复位,这样也太麻烦了. 其实asp.net core团队肯定已经考虑到了这种需求,比如记录请求响应日志.给响 ...
- ASP.NET Core 中的 ObjectPool 对象重用(一)
前言 对象池是一种设计模式,一个对象池包含一组已经初始化过且可以使用的对象,而可以在有需求时创建和销毁对象.池的对象可以从池中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁他,他是一种 ...
- ASP.NET Core 中的 ObjectPool 对象重用(二)
前言 上一篇文章主要介绍了ObjectPool的理论知识,再来介绍一下Microsoft.Extensions.ObjectPool是如何实现的. 核心组件 ObjectPool ObjectPool ...
- Asp.net Core Session 存储任意对象
using Microsoft.AspNetCore.Http; using Newtonsoft.Json; public static class SessionExtensions { publ ...
- asp.net core 自定义异常处理中间件
asp.net core 自定义异常处理中间件 Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异 ...
- 如何在ASP.NET Core程序启动时运行异步任务(1)
原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...
- 避免在ASP.NET Core 3.0中为启动类注入服务
本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...
随机推荐
- Java高效开发-常用idea插件
这里推荐几个常用的idea插件,可以大大提高开发的效率 所使用的idea版本为 2021.2.3 1.Jrebel and Xrebel for IntelliJ Jrebel用于热部署,避免频繁重启 ...
- 扩容新生代为什么能够提高GC的效率
扩容新生代为什么能够提高GC的效率 该文章默认读者对JVM的基础有所了解 在学习JVM的时候,遇到了个人感觉比较有意思的问题,通过视频学习整理了一下. 先来上图: 大部分情况下,对象都会进入Eden区 ...
- [JavaWeb]Log4j的前因后果
Log4j的前因后果 简介 Log4j的进化史 Log4J的三大组件: Logger:日志记录器,负责收集处理日志记录 (如何处理日志) Appender:日志输出目的地,负责日志的输出 (输出到什么 ...
- java代码实现调用短信接口,发送短信验证。
一.代码示例 package com.aaa.zxf.login; import org.apache.commons.httpclient.HttpClient; import org.apache ...
- 根据指定手绘图纸照片行政区划自定义绘制对应区域边界生成geoJOSN的解决方案
项目需求 指定某区域(非省市区县乡镇标准行政区划),做功能边界分区,实现Echarts地理坐标数据可视化展示. 提供信息 项目分析 要在Echarts上实现地理坐标可视化,必须使用geoJSON格式文 ...
- Linux命令date日期时间和Unix时间戳互转
A.将日期转换为Unix时间戳将当前时间以Unix时间戳表示: date +%s 输出如下: 1361542433 转换指定日期为Unix时间戳: date -d '2013-2-22 22:14' ...
- Apache——网页优化与安全
Apache--网页优化与安全 1.Apache 网页优化概述 2.网页压缩 3.网页缓存 4.隐藏版本信息 5.Apache 防盗链 1.Apache 网页优化概述: 企业中,部署Apache后只采 ...
- Linux组管理
首先查看文件所有者.文件所在组等信息:ls -l 1.改变文件所有者:chown 用户名 文件名 执行 chown xm Hello.java 后,可以看到文件所有者现在是属于xm这个用户的了 2.改 ...
- ASP.NET Core 6框架揭秘实例演示[02]:基于路由、MVC和gRPC的应用开发
ASP.NET Core可以视为一种底层框架,它为我们构建出了基于管道的请求处理模型,这个管道由一个服务器和多个中间件构成,而与路由相关的EndpointRoutingMiddleware和Endpo ...
- spring mvc通过客户端传值,controller获取Sort对象
之前客户端需要根据需求按不同的排序方式查看数据,按照一种约定排序,比如1代表时间升序,2代表时间降序,3,4这种形式,然后后台根据这些值创建Sort对象. 后来发现完全多此一举,可以根据特定的方式,直 ...