首页
3D照片墙
统计
留言
Search
1
1.OAuth 的简单理解
115 阅读
2
多个拦截器的执行顺序
105 阅读
3
基于Annotation方式的声明式事务
102 阅读
4
6.设计模式汇总
101 阅读
5
Unity 依赖注入
98 阅读
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
Search
标签搜索
排序算法
vue
算法
遍历
docker
线性
数组
dom
synchronized
数据库
xml语言
log4j
bigint
静态函数
静态方法
哈夫曼树
const
冒泡排序
商标设计
命令模式
Bi8bo
累计撰写
304
篇文章
累计收到
6
条评论
首页
栏目
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
页面
3D照片墙
统计
留言
搜索到
7
篇与
的结果
2024-11-30
管道处理模型
暂无简介
2024年11月30日
9 阅读
0 评论
58 点赞
2024-09-30
源码学习:Routing 路由
前言 最近打算抽时间看一下 ASP.NET Core MVC 的源码,特此把自己学习到的内容记录下来,也算是做个笔记吧。 路由作为 MVC 的基本部分,所以在学习 MVC 的其他源码之前还是先学习一下路由系统,ASP.NET Core 的路由系统相对于以前的 Mvc 变化很大,它重新整合了 Web Api 和 MVC。 路由源码地址 :https://github.com/aspnet/Routing 路由(Routing)功能介绍 路由是 MVC 的一个重要组成部分,它主要负责将接收到的 Http 请求映射到具体的一个路由处理程序上,在MVC 中也就是说路由到具体的某个 Controller 的 Action 上。 路由的启动方式是在ASP.NET Core MVC 应用程序启动的时候作为一个中间件来启动的,详细信息会在下一篇的文章中给出。 通俗的来说就是,路由从请求的 URL 地址中提取信息,然后根据这些信息进行匹配,从而映射到具体的处理程序上,因此路由是基于URL构建的一个中间件框架。 路由还有一个作用是生成响应的的URL,也就是说生成一个链接地址可以进行重定向或者链接。 路由中间件主要包含以下几个部分: URL 匹配 URL 生成 IRouter 接口 路由模板 模板约束 Getting Started ASP.NET Core Routing 主要分为两个项目,分别是 Microsoft.AspNetCore.Routing.Abstractions,Microsoft.AspNetCore.Routing。前者是一个路由提供各功能的抽象,后者是具体实现。 我们在阅读源码的过程中,我建议还是先大致浏览一下项目结构,然后找出关键类,再由入口程序进行阅读。 Microsoft.AspNetCore.Routing.Abstractions 大致看完整个结构之后,我可能发现了几个关键的接口,理解了这几个接口的作用后能够帮助我们在后续的阅读中事半功倍。 IRouter 在 Microsoft.AspNetCore.Routing.Abstractions 中有一个关键的接口就是 IRouter: public interface IRouter { Task RouteAsync(RouteContext context); VirtualPathData GetVirtualPath(VirtualPathContext context); } 这个接口主要干两件事情,第一件是根据路由上下文来进行路由处理,第二件是根据虚拟路径上下文获取 VirtualPathData。 IRouteHandler 另外一个关键接口是 IRouteHandler , 根据名字可以看出主要是对路由处理程序机型抽象以及定义的一个接口。 public interface IRouteHandler { RequestDelegate GetRequestHandler(HttpContext httpContext, RouteData routeData); } 它返回一个 RequestDelegate 的一个委托,这个委托可能大家比较熟悉了,封装了处理Http请求的方法,位于Microsoft.AspNetCore.Http.Abstractions 中,看过我之前博客的同学应该比较了解。 这个接口中 GetRequestHandler 方法有两个参数,第一个是 HttpContext,就不多说了,主要是来看一下第二个参数 RouteData。 RouteData,封装了当前路由中的数据信息,它包含三个主要属性,分别是 DataTokens, Routers, Values。 DataTokens: 是匹配的路径中附带的一些相关属性的键值对字典。 Routers: 是一个 Ilist 列表,说明RouteData 中可能会包含子路由。 Values: 当前路由的路径下包含的键值。 还有一个 RouteValueDictionary, 它是一个集合类,主要是用来存放路由中的一些数据信息的,没有直接使用 IEnumerable<KeyValuePair<string, string>> 这个数据结构是应为它的内部存储转换比较复杂,它的构造函数接收一个 Object 的对象,它会尝试将Object 对象转化为自己可以识别的集合。 IRoutingFeature 我根据这个接口的命名一眼就看出来了这个接口的用途,还记得我在之前博客中讲述Http管道流程中得时候提到过一个叫 工具箱 的东西么,这个 IRoutingFeature 也是其中的一个组成部分。我们看一下它的定义: public interface IRoutingFeature { RouteData RouteData { get; set; } } 原来他只是包装了 RouteData,到 HttpContext 中啊。 IRouteConstraint 这个接口我在阅读的时候看了一下注释,原来路由中的参数参数检查主要是靠这个接口来完成的。 我们都知道在我们写一个 Route Url地址表达式的时候,有时候会这样写:Route("/Product/{ProductId:long}") , 在这个表达式中有一个 {ProductId:long} 的参数约束,那么它的主要功能实现就是靠这个接口来完成的。 /// Defines the contract that a class must implement in order to check whether a URL parameter /// value is valid for a constraint. public interface IRouteConstraint { bool Match( HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection); } Microsoft.AspNetCore.Routing Microsoft.AspNetCore.Routing 主要是对 Abstractions 的一个主要实现,我们阅读代码的时候可以从它的入口开始阅读。 RoutingServiceCollectionExtensions 是一个扩展ASP.NET Core DI 的一个扩展类,在这个方法中用来进行 ConfigService,Routing 对外暴露了一个 IRoutingBuilder 接口用来让用户添加自己的路由规则,我们来看一下: public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, Action<IRouteBuilder> action) { //...略 // 构造一个RouterBuilder 提供给action委托宫配置 var routeBuilder = new RouteBuilder(builder); action(routeBuilder); //调用下面的一个扩展方法,routeBuilder.Build() 见下文 return builder.UseRouter(routeBuilder.Build()); } public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router) { //...略 return builder.UseMiddleware<RouterMiddleware>(router); } routeBuilder.Build() 构建了一个集合 RouteCollection,用来保存所有的 IRouter 处理程序信息,包括用户配置的Router。 RouteCollection 本身也实现了 IRouter , 所以它也具有路由处理的能力。 Routing 中间件的入口是 RouterMiddleware 这个类,通过这个中间件注册到 Http 的管道处理流程中, ASP.NET Core MVC 会把它默认的作为其配置项的一部分,当然你也可以把Routing单独拿出来使用。 我们来看一下 Invoke 方法里面做了什么,它位于RouterMiddleware.cs 文件中。 public async Task Invoke(HttpContext httpContext) { var context = new RouteContext(httpContext); context.RouteData.Routers.Add(_router); await _router.RouteAsync(context); if (context.Handler == null) { _logger.RequestDidNotMatchRoutes(); await _next.Invoke(httpContext); } else { httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature() { RouteData = context.RouteData, }; await context.Handler(context.HttpContext); } } 首先,通过 httpContext 来初始化路由上下文(RouteContext),然后把用户配置的路由规则添加到路由上下文RouteData中的Routers中去。 接下来 await _router.RouteAsync(context) , 就是用到了 IRouter 接口中的 RouteAsync 方法了。 我们接着跟踪 RouteAsync 这个函数,看其内部都做了什么? 我们又跟踪到了RouteCollection.cs 这个类: 我们看一下 RouteAsync 的流程: public async virtual Task RouteAsync(RouteContext context) { var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null); for (var i = 0; i < Count; i++) { var route = this[i]; context.RouteData.Routers.Add(route); try { await route.RouteAsync(context); if (context.Handler != null) { break; } } finally { if (context.Handler == null) { snapshot.Restore(); } } } } 我觉得这个类,包括函数设计的很巧妙,如果是我的话,我不一定能够想的出来,所以我们通过看源码也能够学到很多新知识。 为什么说设计的巧妙呢? RouteCollection 继承了 IRouter 但是并没有具体的对路由进行处理,而是通过循环来重新将路由上下文分发的具体的路由处理程序上。我们来看一下他的流程: 1、为了提高性能,创建了一个RouteDataSnapshot 快照对象,RouteDataSnapshot是一个结构体,它存储了 Route 中的路由数据信息。 2、循环当前 RouteCollection 中的 Router,添加到 RouterContext里的Routers中,然后把RouterContext交给Router来处理。 3、当没有处理程序处理当前路由 snapshot.Restore() 重新初始化快照状态。 接下来就要看具体的路由处理对象了,我们从 RouteBase 开始。 1、RouteBase 的构造函数会初始化 RouteTemplate, Name, DataTokens, Defaults. Defaults 是默认配置的路由参数。 2、RouteAsync 中会进行一系列检查,如果没有匹配到URL对应的路由就会直接返回。 3、使用路由参数匹配器 RouteConstraintMatcher 进行匹配,如果没有匹配到,同样直接返回。 4、如果匹配成功,会触发 OnRouteMatched(RouteContext context)函数,它是一个抽象函数,具体实现位于 Route.cs 中。 然后,我们再继续跟踪到 Route.cs 中的 OnRouteMatch,一起来看一下: protected override Task OnRouteMatched(RouteContext context) { context.RouteData.Routers.Add(_target); return _target.RouteAsync(context); } _target 值得当前路由的处理程序,那么具体是哪个路由处理程序呢? 我们一起探索一下。 我们知道,我们创建路由一共有MapRoute,MapGet,MapPost,MapPut,MapDelete,MapVerb... 等等这写方式,我们分别对应说一下每一种它的路由处理程序是怎么样的,下面是一个示例: app.UseRouter(routes =>{ routes.DefaultHandler = new RouteHandler((httpContext) => { var request = httpContext.Request; return httpContext.Response.WriteAsync($""); }); routes .MapGet("api/get/{id}", (request, response, routeData) => {}) .MapMiddlewareRoute("api/middleware", (appBuilder) => appBuilder.Use((httpContext, next) => httpContext.Response.WriteAsync("Middleware!") )) .MapRoute( name: "AllVerbs", template: "api/all/{name}/{lastName?}", defaults: new { lastName = "Doe" }, constraints: new { lastName = new RegexRouteConstraint(new Regex("[a-zA-Z]{3}",RegexOptions.CultureInvariant, RegexMatchTimeout)) }); }); 按照上面的示例解释一下, MapRoute:使用这种方式的话,必须要给 DefaultHandler 赋值处理程序,否则会抛出异常,通常情况下我们会使用RouteHandler类。 MapVerb: MapPost,MapPut 等等都和它类似,它将处理程序作为一个 RequestDelegate 委托提供了出来,也就是说我们实际上在自己处理HttpContext的东西,不会经过RouteHandler处理。 MapMiddlewareRoute:需要传入一个 IApplicationBuilder 委托,实际上 IApplicationBuilder Build之后也是一个 RequestDelegate,它会在内部 new 一个 RouteHandler 类,然后调用的 MapRoute。 这些所有的矛头都指向了 RouteHandler , 我们来看看 RouteHandler 吧。 public class RouteHandler : IRouteHandler, IRouter { // ...略 public Task RouteAsync(RouteContext context) { context.Handler = _requestDelegate; return TaskCache.CompletedTask; } } 什么都没干,仅仅是将传入进来的 RequestDelegate 赋值给了 RouteContext 的处理程序。 最后,代码会执行到 RouterMiddleware 类中的 Invoke 方法的最后一行 await context.Handler(context.HttpContext),这个时候开始调用Handler委托,执行用户代码。 总结 我们来总结一下以上流程: 首先传入请求会到注册的 RouterMiddleware 中间件,然后它 RouteAsync 按顺序调用每个路由上的方法。当一个请求到来的时候,IRouter实例选择是否处理已经设置到 RouteContext Handler 上的一个非空 RequestDelegate。如果Route已经为该请求设置处理程序,则路由处理会中止并且开始调用设置的Hanlder处理程序以处理请求。如果当前请求尝试了所有路由都没有找到处理程序的话,则调用next,将请求交给管道中的下一个中间件。
2024年09月30日
95 阅读
0 评论
22 点赞
2024-08-20
EF Core 构建
安装几个包 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.Tools Microsoft.EntityFrameworkCore.Design 生成数据库 Scaffold-DbContext "user id=sa;password=123456;initial catalog=GameShow;data source=." Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
2024年08月20日
21 阅读
0 评论
84 点赞
2024-07-03
async await Wait() Result死锁原因
【C#(8)Task异步多线程调用中的死锁】https://www.bilibili.com/video/BV1Rs4y1f7Y8?vd_source=4a095c6449d5f55cf511adce20a14f28
2024年07月03日
39 阅读
0 评论
100 点赞
2023-10-20
.Net Core 6过滤器
Filters in ASP.NET Core 官网地址:https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-2.2 Filter ASP.NET Core 中的过滤器*允许代码在请求处理管道中的特定阶段之前或之后运行。 内置过滤器处理以下任务: 授权,防止用户访问未经授权的资源。 响应缓存,将请求管道短路以返回缓存的响应。 可以创建自定义过滤器来处理横切关注点。横切关注点的示例包括错误处理、缓存、配置、授权和日志记录。过滤器避免重复代码。例如,错误处理异常过滤器可以合并错误处理。 本文档适用于 Razor 页面、API 控制器和具有视图的控制器。过滤器不能直接与Razor 组件一起使用。过滤器只能在以下情况下间接影响组件: 该组件嵌入在页面或视图中。 页面或控制器和视图使用过滤器。 过滤器的工作原理 筛选器在ASP.NET Core 操作调用管道(有时称为筛选器管道)中运行。过滤器管道在 ASP.NET Core 选择要执行的操作后运行: 过滤器类型 每个过滤器类型在过滤器管道的不同阶段执行: Authorization filters run first 最先执行 确定用户是否有权执行请求。 如果请求未经授权,则将管道短路。 Resource filters 授权后运行。 OnResourceExecuting 在过滤器管道的其余部分之前运行代码。例如,OnResourceExecuting在模型绑定之前运行代码。 - OnResourceExecuted 在管道的其余部分完成后运行代码。 Action filters 在调用操作方法之前和之后立即运行。 可以更改传递给动作的参数。 可以更改从动作返回的结果。 Razor页面 不支持。 Exception filters 将全局策略应用于在响应正文被写入之前发生的未处理异常。 Result filters 在执行动作结果之前和之后立即运行。 仅在操作方法成功执行时运行。 对于必须围绕视图或格式化程序执行的逻辑很有用。 下图显示了过滤器类型如何在过滤器管道中交互: 总结: Authorization filters、Exception filters没有前后2种方法(仅限同步方法) Resource filters、Action filters、Result filters 有前后2种方法(仅限同步方法) 如果是异步的,则与中间件类似 before //等同于xxxExecuting await next();//action 执行xxx方法 after //等同于xxxExecuted Razor Pages 还支持Razor Page 过滤器,它们在 Razor Page 处理程序之前和之后运行。 例子:ActionFilters 同步 using Microsoft.AspNetCore.Mvc.Filters; namespace FilterMvc.Filters { public class SampleActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // Do something before the action executes. Console.WriteLine("执行方法前做点什么呢?执行方法前"); } public void OnActionExecuted(ActionExecutedContext context) { // Do something after the action executes. Console.WriteLine("执行方法后做点什么呢?执行方法后"); } } } 异步: using Microsoft.AspNetCore.Mvc.Filters; namespace FilterMvc.Filters { public class SampleAsyncActionFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // Do something before the action executes. Console.WriteLine("执行方法前做点什么呢?执行方法前"); await next(); // Do something after the action executes. Console.WriteLine("执行方法后做点什么呢?执行方法后"); } } } Multiple filter stages(复式filter) 多个过滤级的接口可以在单个类中实现。例如,ActionFilterAttribute类实现: 同步: IActionFilter和IResultFilter 异步: IAsyncActionFilter和IAsyncResultFilter IOrderedFilter 实现过滤器接口的同步或异步版本,而不是两者。运行时首先检查过滤器是否实现了异步接口,如果是,则调用它。如果不是,它调用同步接口的方法。如果在一个类中同时实现异步和同步接口,则只调用 async 方法。使用ActionFilterAttribute等抽象类时,仅覆盖每个过滤器类型的同步方法或异步方法。 内置Attributes Filter (Built-in filter attributes) ASP.NET Core 包括内置的基于属性的过滤器,可以进行子类化和自定义。例如,以下结果过滤器会在响应中添加一个标头: public class ResponseHeaderAttribute : ActionFilterAttribute { private readonly string _name; private readonly string _value; public ResponseHeaderAttribute(string name, string value) => (_name, _value) = (name, value); public override void OnResultExecuting(ResultExecutingContext context) { context.HttpContext.Response.Headers.Add(_name, _value); base.OnResultExecuting(context); } } 属性允许过滤器接受参数,如前面的示例所示。将 应用于ResponseHeaderAttribute控制器或操作方法并指定 HTTP 标头的名称和值: [ResponseHeader("Filter-Header", "Filter Value")] public class ResponseHeaderController : ControllerBase { public IActionResult Index() => Content("Examine the response headers using the F12 developer tools."); // ... 执行结果: using FilterMvc.Filters; using Microsoft.AspNetCore.Mvc; namespace FilterMvc.Controllers { public class ResponseHeaderController : Controller { /* public IActionResult Index() { return View(); } */ [ResponseHeader("Filter-Header", "Filter Value")] public IActionResult Index() { return Content("Examine the response headers using the F12 developer tools."); } [ResponseHeader("Another-Filter-Header", "Another Filter Value")] public IActionResult Multiple() => Content("Examine the response headers using the F12 developer tools."); } } 该操作的响应Multiple包括以下标头: filter-header: Filter Value another-filter-header: Another Filter Value 一些过滤器接口具有相应的属性,可以用作自定义实现的基类。 Filter attributes: ActionFilterAttribute ExceptionFilterAttribute ResultFilterAttribute FormatFilterAttribute ServiceFilterAttribute TypeFilterAttribute 筛选器不能应用于 Razor 页面处理程序方法。它们可以应用于 Razor Page 模型或全局。 Filter范围和执行顺序(Filter scopes and order of execution) 可以在以下三个范围*之一将过滤器添加到管道中: 使用控制器或 Razor 页面上的属性。 在控制器操作上使用属性。筛选器属性不能应用于 Razor 页面处理程序方法。 全局用于所有控制器、操作和 Razor 页面,如以下代码所示: var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); }); 默认执行顺序 当管道的特定阶段有多个过滤器时,范围确定过滤器执行的默认顺序。全局过滤器围绕着类过滤器,而后者又围绕着方法过滤器。 由于过滤器嵌套,过滤器的后代码以与前**代码相反的顺序运行。过滤顺序: 以下示例说明了过滤器方法为同步操作过滤器运行的顺序: 序列 Filter scope Filter method 1 Global OnActionExecuting 2 Controller OnActionExecuting 3 Action OnActionExecuting 4 Action OnActionExecuted 5 Controller OnActionExecuted 6 Global OnActionExecuted 可以通过实现IOrderedFilter来覆盖默认的执行顺序。IOrderedFilter公开Order属性,该属性优先于范围以确定执行顺序。具有较低Order值的过滤器: 在具有较高值的过滤器之前运行 之前Order的代码。 在具有更高值的过滤器之后运行 after代码。Order 在控制器级别过滤器示例中,GlobalSampleActionFilter具有全局范围,因此它在SampleActionFilterAttribute具有控制器范围之前运行。要先运行,请将SampleActionFilterAttribute其顺序设置为int.MinValue: [SampleActionFilter(Order = int.MinValue)] public class ControllerFiltersController : Controller { // ... } GlobalSampleActionFilter要首先运行全局过滤器,请将其设置Order为int.MinValue: builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(int.MinValue); }); 顺序总结:(这个指的是同一级别的Filter) order值越小越先执行,由于是before action after 所以越小的after越后执行,和中间件类似 能用中间件实现的功能,优先考虑中间件,如果在Mvc里面的需求再考虑Filter 原文地址:https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters.iorderedfilter.order?view=aspnetcore-6.0#microsoft-aspnetcore-mvc-filters-iorderedfilter-order 取消和短路 可以通过在提供给过滤器方法的ResourceExecutingContext参数上设置Result属性来短路过滤器管道。例如,以下资源过滤器会阻止管道的其余部分执行: public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter { public void OnResourceExecuting(ResourceExecutingContext context) { //设置Result属性即可使ResourceFilter 之后的过滤器都不执行 在这里短路了 context.Result = new ContentResult { Content = nameof(ShortCircuitingResourceFilterAttribute) }; } public void OnResourceExecuted(ResourceExecutedContext context) { } } 在下面的代码中,[ShortCircuitingResourceFilter]和[ResponseHeader]filter 都以Indexaction 方法为目标。ShortCircuitingResourceFilterAttribute过滤器: 首先运行,因为它是资源过滤器并且 ResponseHeaderAttribute是操作过滤器。 使管道的其余部分短路。 因此ResponseHeaderAttribute过滤器永远不会运行该Index操作。如果两个过滤器都在操作方法级别应用,则此行为将是相同的,前提是ShortCircuitingResourceFilterAttribute先运行。首先ShortCircuitingResourceFilterAttribute运行是因为它的过滤器类型: [ResponseHeader("Filter-Header", "Filter Value")] public class ShortCircuitingController : Controller { [ShortCircuitingResourceFilter] public IActionResult Index() => Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}"); } 依赖注入 过滤器可以按类型或实例添加。如果添加了一个实例,则该实例将用于每个请求。如果添加了类型,则它是类型激活的。类型激活过滤器意味着: 为每个请求创建一个实例。 任何构造函数依赖项都通过依赖项注入 (DI)填充。 作为属性实现并直接添加到控制器类或操作方法的过滤器不能具有依赖注入(DI) 提供的构造函数依赖项。构造函数依赖项不能由 DI 提供,因为属性必须在应用它们的地方提供其构造函数参数。 以下过滤器支持 DI 提供的构造函数依赖项: ServiceFilterAttribute TypeFilterAttribute IFilterFactory 在属性上实现 前面的过滤器可以应用于控制器或动作。 记录仪可从 DI 获得。但是,请避免纯粹出于日志记录目的而创建和使用过滤器。内置框架日志记录通常提供日志记录所需的内容。日志添加到过滤器: 应该关注特定于过滤器的业务领域问题或行为。 不应 记录操作或其他框架事件。内置过滤器已经记录了操作和框架事件。 服务过滤器属性(ServiceFilterAttribute) 服务过滤器实现类型注册在Program.cs. ServiceFilterAttribute从 DI 检索过滤器的实例。 以下代码显示了LoggingResponseHeaderFilterService使用 DI 的类: public class LoggingResponseHeaderFilterService : IResultFilter { private readonly ILogger _logger; public LoggingResponseHeaderFilterService( ILogger<LoggingResponseHeaderFilterService> logger) => _logger = logger; public void OnResultExecuting(ResultExecutingContext context) { _logger.LogInformation( $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}"); context.HttpContext.Response.Headers.Add( nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService)); } public void OnResultExecuted(ResultExecutedContext context) { _logger.LogInformation( $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}"); } } 在以下代码中,LoggingResponseHeaderFilterService添加到 DI 容器中: builder.Services.AddScoped<LoggingResponseHeaderFilterService>(); 在以下代码中,属性从 DIServiceFilter检索过滤器的实例:LoggingResponseHeaderFilterService [ServiceFilter(typeof(LoggingResponseHeaderFilterService))] public IActionResult WithServiceFilter() => Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}"); 使用时ServiceFilterAttribute,设置ServiceFilterAttribute.IsReusable: 提供过滤器实例可以在创建它的请求范围之外重用的提示。ASP.NET Core 运行时不保证: 将创建过滤器的单个实例。 稍后将不会从 DI 容器重新请求过滤器。 不应与依赖于生命周期而非单例的服务的过滤器一起使用。 ServiceFilterAttribute实现IFilterFactory。IFilterFactory公开用于创建IFilterMetadata实例的CreateInstance方法。从 DI 加载指定的类型。CreateInstance 总结 在要使用的地方如上述加上[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]即可 执行结果: 类型过滤器属性(TypeFilterAttribute) TypeFilterAttribute类似于ServiceFilterAttribute,但它的类型不是直接从 DI 容器解析的。它使用Microsoft.Extensions.DependencyInjection.ObjectFactory实例化类型。 因为TypeFilterAttribute类型不是直接从 DI 容器中解析的: 使用 引用的类型 TypeFilterAttribute不需要在 DI 容器中注册。它们的依赖项确实由 DI 容器实现。 TypeFilterAttribute 可以选择接受该类型的构造函数参数。 使用时TypeFilterAttribute,设置TypeFilterAttribute.IsReusable: 提供过滤器实例 可以在创建它的请求范围之外重用的提示。ASP.NET Core 运行时不保证将创建过滤器的单个实例。 不应与依赖于生命周期而非单例的服务的过滤器一起使用。 Filter: using Microsoft.AspNetCore.Mvc.Filters; namespace FilterMvc.Filters { public class LoggingResponseHeaderFilter : IActionFilter { private readonly string _name; private readonly string _value; public LoggingResponseHeaderFilter(string name, string value) => (_name, _value) = (name, value); public void OnActionExecuted(ActionExecutedContext context) { Console.WriteLine("=============方法执行后==========="); } public void OnActionExecuting(ActionExecutingContext context) { Console.WriteLine("=============方法执行前==========="); context.HttpContext.Response.Headers.Add(_name, _value); } } } 以下示例显示了如何使用 将参数传递给类型TypeFilterAttribute: [TypeFilter(typeof(LoggingResponseHeaderFilter), Arguments = new object[] { "Filter-Header", "Filter Value" })] public IActionResult WithTypeFilter() => Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}"); 执行结果: 过滤器工厂(IFilterFactory) IFilterFactory实现IFilterMetadata。因此,IFilterFactory实例可以用作IFilterMetadata过滤器管道中任何位置的实例。当运行时准备调用过滤器时,它会尝试将其转换为IFilterFactory. 如果该转换成功,则调用CreateInstance方法来创建所IFilterMetadata调用的实例。这提供了一种灵活的设计,因为在应用程序启动时不需要明确设置精确的过滤器管道。 IFilterFactory.IsReusable: 工厂提示工厂创建的过滤器实例可以在创建它的请求范围之外重用。 不应与依赖于生命周期而非单例的服务的过滤器一起使用 。 ASP.NET Core 运行时不保证: 将创建过滤器的单个实例。 稍后将不会从 DI 容器重新请求过滤器。 警告 仅当过滤器的来源明确、过滤器是无状态的并且过滤器可以安全地跨多个 HTTP 请求使用时,才将IFilterFactory.IsReusable配置为返回。例如,如果返回true,则不要从注册为作用域或瞬态的 DI 中返回过滤器。IFilterFactory.IsReusable``true IFilterFactory可以使用自定义属性实现作为创建过滤器的另一种方法来实现: public class ResponseHeaderFilterFactory : Attribute, IFilterFactory { public bool IsReusable => false; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => new InternalResponseHeaderFilter(); private class InternalResponseHeaderFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) => context.HttpContext.Response.Headers.Add( nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter)); public void OnActionExecuted(ActionExecutedContext context) { } } 过滤器应用在以下代码中: [ResponseHeaderFilterFactory] public IActionResult Index() => Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}"); 总结: IFilterFactory.IsReusable(可复用) 这个属性直接关系到生命周期以及使用场景。根据自己具体项目来看 True则时单例,不能与非单例的Filter一起使用。 IFilterFactory 在属性(Attribute)上实现 实现IFilterFactory的过滤器对于以下过滤器很有用: 不需要传递参数。 具有需要由 DI 填充的构造函数依赖项。 TypeFilterAttribute实现IFilterFactory。IFilterFactory公开用于创建IFilterMetadata实例的CreateInstance方法。从服务容器 (DI) 加载指定的类型。CreateInstance public class SampleActionTypeFilterAttribute : TypeFilterAttribute { public SampleActionTypeFilterAttribute() : base(typeof(InternalSampleActionFilter)) { } private class InternalSampleActionFilter : IActionFilter { private readonly ILogger<InternalSampleActionFilter> _logger; public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) => _logger = logger; public void OnActionExecuting(ActionExecutingContext context) { _logger.LogInformation( $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}"); } public void OnActionExecuted(ActionExecutedContext context) { _logger.LogInformation( $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}"); } } } 以下代码显示了三种应用过滤器的方法: [SampleActionTypeFilter]//推荐使用这种 public IActionResult WithDirectAttribute() => Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}"); [TypeFilter(typeof(SampleActionTypeFilterAttribute))] public IActionResult WithTypeFilterAttribute() => Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}"); [ServiceFilter(typeof(SampleActionTypeFilterAttribute))] public IActionResult WithServiceFilterAttribute() => Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}"); 在前面的代码中,首选应用过滤器的第一种方法。 在过滤器管道中使用中间件 资源过滤器像中间件一样工作,因为它们围绕管道中稍后出现的所有内容的执行。但是过滤器与中间件的不同之处在于它们是运行时的一部分,这意味着它们可以访问上下文和构造。 要将中间件用作过滤器,请使用Configure指定要注入过滤器管道的中间件的方法创建一个类型。以下示例使用中间件设置响应标头: public class FilterMiddlewarePipeline { public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { context.Response.Headers.Add("Pipeline", "Middleware"); await next(); }); } } 使用MiddlewareFilterAttribute运行中间件: [MiddlewareFilter(typeof(FilterMiddlewarePipeline))]//MiddlewareFilter public class FilterMiddlewareController : Controller { public IActionResult Index() => Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}"); } 中间件过滤器与资源过滤器在过滤器管道的同一阶段运行,在模型绑定之前和管道的其余部分之后。 执行结果 线程安全 When passing an instance of a filter into Add, instead of its Type, the filter is a singleton and is not thread-safe. 当将过滤器的实例Add传递给而不是其Type时,过滤器是单例并且不是线程安全的。(机翻) 例子: T t = new T(); Add(t); 授权过滤器 授权过滤器: 第一个过滤器是否在过滤器管道中运行。 控制对操作方法的访问。 有 before 方法,但没有 after 方法。 自定义授权过滤器需要自定义授权框架。优先配置授权策略或编写自定义授权策略而不是编写自定义过滤器。内置授权过滤器: 调用授权系统。 不授权请求。 不要在授权过滤器中抛出异常: 将不处理异常。 异常过滤器不会处理异常。 考虑在授权过滤器中发生异常时发出质询。 了解有关授权的更多信息。 资源过滤器 资源过滤器: 实现 IResourceFilter或IAsyncResourceFilter接口。 执行包装了大部分过滤器管道。 只有 授权过滤器在资源过滤器之前运行。 资源过滤器可用于使大部分管道短路。例如,缓存过滤器可以避免缓存命中的其余管道。 资源过滤器示例: 前面显示的短路资源过滤器 。 DisableFormValueModelBindingAttribute : - 防止模型绑定访问表单数据。 - 用于大文件上传,防止表单数据被读入内存。 动作过滤器 操作过滤器不适用于 Razor 页面。Razor Pages 支持IPageFilter和IAsyncPageFilter。有关详细信息,请参阅Razor 页面的筛选方法。 动作过滤器: 实现 IActionFilter或IAsyncActionFilter接口。 它们的执行围绕动作方法的执行。 以下代码显示了一个示例操作过滤器: public class SampleActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // Do something before the action executes. } public void OnActionExecuted(ActionExecutedContext context) { // Do something after the action executes. } } ActionExecutingContext提供以下属性: ActionArguments - 允许读取操作方法的输入。 控制器 启用操纵控制器实例。 结果 设置Result短路执行操作方法和后续操作过滤器。 在操作方法中抛出异常: 防止运行后续过滤器。 与设置不同 Result,被视为失败而不是成功的结果。 ActionExecutedContext提供并Controller加上Result以下属性: Canceled - 如果操作执行被另一个过滤器短路,则为真。 Exception - 如果操作或先前运行的操作过滤器引发异常,则为非 null。将此属性设置为 null: - 有效地处理异常。 - Result 就像从 action 方法返回一样执行。 对于一个IAsyncActionFilter,调用ActionExecutionDelegate: 执行任何后续操作过滤器和操作方法。 returns ActionExecutedContext。 要短路,请将Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result分配给结果实例并且不要调用next( ActionExecutionDelegate)。 该框架提供了一个可以被子类化的抽象ActionFilterAttribute 。 动作过滤器OnActionExecuting可用于: 验证模型状态。 如果状态无效,则返回错误。 public class ValidateModelAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { context.Result = new BadRequestObjectResult(context.ModelState); } } } [ApiController]使用该属性注释的控制器会自动验证模型状态并返回 400 响应。有关详细信息,请参阅自动 HTTP 400 响应。 该OnActionExecuted方法在操作方法之后运行: 并且可以通过Result 属性查看和操作动作的结果。 如果操作执行被另一个过滤器短路,则 Canceled设置为 true。 如果操作或后续操作过滤器引发异常,则将Exception设置为非空值。设置Exception为空: 有效地处理异常。 ActionExecutedContext.Result 就像从 action 方法正常返回一样执行。 异常过滤器 异常过滤器: 实施 IExceptionFilter或IAsyncExceptionFilter。 可用于实现常见的错误处理策略。 以下示例异常筛选器显示有关在开发应用程序时发生的异常的详细信息: public class SampleExceptionFilter : IExceptionFilter { private readonly IHostEnvironment _hostEnvironment; public SampleExceptionFilter(IHostEnvironment hostEnvironment) => _hostEnvironment = hostEnvironment; public void OnException(ExceptionContext context) { if (!_hostEnvironment.IsDevelopment()) { // Don't display exception details unless running in Development. return; } context.Result = new ContentResult { Content = context.Exception.ToString() }; } } 以下代码测试异常过滤器: [TypeFilter(typeof(SampleExceptionFilter))] public class ExceptionController : Controller { public IActionResult Index() => Content($"- {nameof(ExceptionController)}.{nameof(Index)}"); } 异常过滤器: 没有前后事件。 实施 OnException或OnExceptionAsync。 处理 Razor 页面或控制器创建、模型绑定 、操作过滤器或操作方法中发生的未处理异常。 不要捕获资源过滤器、结果过滤器或 MVC 结果执行中 发生的异常。 要处理异常,请将ExceptionHandled属性设置为true或分配Result属性。这会阻止异常的传播。异常过滤器不能将异常转化为“成功”。只有动作过滤器可以做到这一点。 异常过滤器: 适用于捕获动作中发生的异常。 不如错误处理中间件灵活。 首选中间件进行异常处理。仅在错误处理因调用的操作方法而异时才使用异常过滤器。例如,一个应用程序可能具有 API 端点和视图/HTML 的操作方法。API 端点可以以 JSON 形式返回错误信息,而基于视图的操作可以以 HTML 形式返回错误页面。 结果过滤器 结果过滤器: 实现一个接口: IResultFilter 或IAsyncResultFilter - IAlwaysRunResultFilter 或IAsyncAlwaysRunResultFilter 它们的执行围绕着行动结果的执行。 IResultFilter 和 IAsyncResultFilter 以下代码显示了一个示例结果过滤器: public class SampleResultFilter : IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { // Do something before the result executes. } public void OnResultExecuted(ResultExecutedContext context) { // Do something after the result executes. } } 执行的结果类型取决于操作。返回视图的操作包括所有 razor 处理作为正在执行的ViewResult的一部分。作为结果执行的一部分,API 方法可能会执行一些序列化。了解有关行动结果的更多信息。 结果过滤器仅在操作或操作过滤器产生操作结果时执行。在以下情况下不执行结果过滤器: 授权过滤器或资源过滤器使管道短路。 异常过滤器通过生成操作结果来处理异常。 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting方法可以通过将Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel设置为来短路操作结果和后续结果筛选器的执行true。短路时写入响应对象以避免生成空响应。抛出异常IResultFilter.OnResultExecuting: 阻止执行操作结果和后续过滤器。 被视为失败而不是成功的结果。 当Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted方法运行时,响应可能已经发送到客户端。如果响应已经发送到客户端,则无法更改。 ResultExecutedContext.Canceled``true如果动作结果执行被另一个过滤器短路,则设置为。 ResultExecutedContext.Exception如果操作结果或后续结果过滤器抛出异常,则设置为非空值。设置Exception为 null 可以有效地处理异常并防止在管道中稍后再次抛出异常。在结果过滤器中处理异常时,没有可靠的方法将数据写入响应。如果在操作结果引发异常时已将标头刷新到客户端,则没有可靠的机制来发送失败代码。 对于IAsyncResultFilterawait next ,对ResultExecutionDelegate的调用将执行任何后续结果过滤器和操作结果。要短路,请将ResultExecutingContext.Cancel设置为true并且不要调用ResultExecutionDelegate: public class SampleAsyncResultFilter : IAsyncResultFilter { public async Task OnResultExecutionAsync( ResultExecutingContext context, ResultExecutionDelegate next) { if (context.Result is not EmptyResult) { await next(); } else { context.Cancel = true; } } } ResultFilterAttribute该框架提供了一个可以被子类化的抽象。前面显示的ResponseHeaderAttribute类是结果过滤器属性的一个示例。 IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter IAlwaysRunResultFilter和IAsyncAlwaysRunResultFilter接口声明了一个针对所有操作结果运行的IResultFilter实现。这包括由以下人员产生的行动结果: 短路的授权过滤器和资源过滤器。 异常过滤器。 例如,当内容协商失败时,以下过滤器始终运行并设置带有422 Unprocessable Entity状态代码的操作结果 ( ObjectResult ): public class UnprocessableResultFilter : IAlwaysRunResultFilter { public void OnResultExecuting(ResultExecutingContext context) { if (context.Result is StatusCodeResult statusCodeResult && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType) { context.Result = new ObjectResult("Unprocessable") { StatusCode = StatusCodes.Status422UnprocessableEntity }; } } public void OnResultExecuted(ResultExecutedContext context) { } }
2023年10月20日
74 阅读
0 评论
66 点赞
1
2