title: Asp.Net Core底层源码剖析(二)过滤器
date: 2022-09-18 10:41:57
categories: 后端
tags:
- .NET

正文

Asp.Net Core中的过滤器有好几种,包括AuthorizationFilter、ActionFilter,ResourceFilter,ExceptionFilter,ResultFilter,平时一般用的多的就是AuthorizationFilter、ActionFilter、ExceptionFilter三个,下面我们写个自定义的ActionFilter,然后debug看一下源码

自定义ActionFilter

我们自定义的ActionFilter可以继承自ActionFilterAttribute,也可以直接实现接口IActionFilter,两者的作用是差不多的,因为ActionFilterAttribute也实现了这个接口,我们看下源码:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute :
Attribute, IActionFilter, IAsyncActionFilter, IResultFilter, IAsyncResultFilter, IOrderedFilter
{
// 实现...
}

可以看出,ActionFilterAttribute继承了好几个接口,下面是这几个接口的简单说明

所以我们直接继承ActionFilterAttribute,其实同时实现了好几个方法:

public class MyActionFilterAttribute:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
Console.WriteLine("OnActionExecuting");
} public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
Console.WriteLine("OnActionExecuted"); } public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
return base.OnActionExecutionAsync(context, next);
Console.WriteLine("OnActionExecutionAsync"); } public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
Console.WriteLine("OnResultExecuting"); } public override void OnResultExecuted(ResultExecutedContext context)
{
base.OnResultExecuted(context);
Console.WriteLine("OnResultExecuted"); } public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
return base.OnResultExecutionAsync(context, next);
Console.WriteLine("OnResultExecuted");
}
}

下面我们把自己写的过滤器添加到全局的过滤器中,而不是直接用在controller或者方法上

builder.Services.AddMvc(it => it.Filters.Add(typeof(MyActionFilterAttribute)));

然后我们开始debug,给OnActionExecuting函数打个断点,然后查看运行的堆栈,我们可以看到是在EndpointMiddleware中间件内部来调用过滤器的

接着我们继续往下,能找到下面这个函数

private Task Next(ref State next, ref Scope scope, ref object? state, ref bool isCompleted)
{
switch (next)
{
case State.InvokeBegin:
{
goto case State.AuthorizationBegin;
} case State.AuthorizationBegin:
{
_cursor.Reset();
goto case State.AuthorizationNext;
} case State.AuthorizationNext:
{
var current = _cursor.GetNextFilter<IAuthorizationFilter, IAsyncAuthorizationFilter>();
if (current.FilterAsync != null)
{
if (_authorizationContext == null)
{
_authorizationContext = new AuthorizationFilterContextSealed(_actionContext, _filters);
} state = current.FilterAsync;
goto case State.AuthorizationAsyncBegin;
}
else if (current.Filter != null)
{
if (_authorizationContext == null)
{
_authorizationContext = new AuthorizationFilterContextSealed(_actionContext, _filters);
} state = current.Filter;
goto case State.AuthorizationSync;
}
else
{
goto case State.AuthorizationEnd;
}
} case State.AuthorizationAsyncBegin:
{
Debug.Assert(state != null);
Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state;
var authorizationContext = _authorizationContext; _diagnosticListener.BeforeOnAuthorizationAsync(authorizationContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.AuthorizationFilter,
nameof(IAsyncAuthorizationFilter.OnAuthorizationAsync),
filter); var task = filter.OnAuthorizationAsync(authorizationContext);
if (!task.IsCompletedSuccessfully)
{
next = State.AuthorizationAsyncEnd;
return task;
} goto case State.AuthorizationAsyncEnd;
} case State.AuthorizationAsyncEnd:
{
Debug.Assert(state != null);
Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state;
var authorizationContext = _authorizationContext; _diagnosticListener.AfterOnAuthorizationAsync(authorizationContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.AuthorizationFilter,
nameof(IAsyncAuthorizationFilter.OnAuthorizationAsync),
filter); if (authorizationContext.Result != null)
{
goto case State.AuthorizationShortCircuit;
} goto case State.AuthorizationNext;
} case State.AuthorizationSync:
{
Debug.Assert(state != null);
Debug.Assert(_authorizationContext != null); var filter = (IAuthorizationFilter)state;
var authorizationContext = _authorizationContext; _diagnosticListener.BeforeOnAuthorization(authorizationContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.AuthorizationFilter,
nameof(IAuthorizationFilter.OnAuthorization),
filter); filter.OnAuthorization(authorizationContext); _diagnosticListener.AfterOnAuthorization(authorizationContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.AuthorizationFilter,
nameof(IAuthorizationFilter.OnAuthorization),
filter); if (authorizationContext.Result != null)
{
goto case State.AuthorizationShortCircuit;
} goto case State.AuthorizationNext;
} case State.AuthorizationShortCircuit:
{
Debug.Assert(state != null);
Debug.Assert(_authorizationContext != null);
Debug.Assert(_authorizationContext.Result != null); _logger.AuthorizationFailure((IFilterMetadata)state); // This is a short-circuit - execute relevant result filters + result and complete this invocation.
isCompleted = true;
_result = _authorizationContext.Result;
return InvokeAlwaysRunResultFilters();
} case State.AuthorizationEnd:
{
goto case State.ResourceBegin;
} case State.ResourceBegin:
{
_cursor.Reset();
goto case State.ResourceNext;
} case State.ResourceNext:
{
var current = _cursor.GetNextFilter<IResourceFilter, IAsyncResourceFilter>();
if (current.FilterAsync != null)
{
if (_resourceExecutingContext == null)
{
_resourceExecutingContext = new ResourceExecutingContextSealed(
_actionContext,
_filters,
_valueProviderFactories);
} state = current.FilterAsync;
goto case State.ResourceAsyncBegin;
}
else if (current.Filter != null)
{
if (_resourceExecutingContext == null)
{
_resourceExecutingContext = new ResourceExecutingContextSealed(
_actionContext,
_filters,
_valueProviderFactories);
} state = current.Filter;
goto case State.ResourceSyncBegin;
}
else
{
// All resource filters are currently on the stack - now execute the 'inside'.
goto case State.ResourceInside;
}
} case State.ResourceAsyncBegin:
{
Debug.Assert(state != null);
Debug.Assert(_resourceExecutingContext != null); var filter = (IAsyncResourceFilter)state;
var resourceExecutingContext = _resourceExecutingContext; _diagnosticListener.BeforeOnResourceExecution(resourceExecutingContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IAsyncResourceFilter.OnResourceExecutionAsync),
filter); var task = filter.OnResourceExecutionAsync(resourceExecutingContext, InvokeNextResourceFilterAwaitedAsync);
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceAsyncEnd;
return task;
} goto case State.ResourceAsyncEnd;
} case State.ResourceAsyncEnd:
{
Debug.Assert(state != null);
Debug.Assert(_resourceExecutingContext != null); var filter = (IAsyncResourceFilter)state;
if (_resourceExecutedContext == null)
{
// If we get here then the filter didn't call 'next' indicating a short circuit.
_resourceExecutedContext = new ResourceExecutedContextSealed(_resourceExecutingContext, _filters)
{
Canceled = true,
Result = _resourceExecutingContext.Result,
}; _diagnosticListener.AfterOnResourceExecution(_resourceExecutedContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IAsyncResourceFilter.OnResourceExecutionAsync),
filter); // A filter could complete a Task without setting a result
if (_resourceExecutingContext.Result != null)
{
goto case State.ResourceShortCircuit;
}
} goto case State.ResourceEnd;
} case State.ResourceSyncBegin:
{
Debug.Assert(state != null);
Debug.Assert(_resourceExecutingContext != null); var filter = (IResourceFilter)state;
var resourceExecutingContext = _resourceExecutingContext; _diagnosticListener.BeforeOnResourceExecuting(resourceExecutingContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IResourceFilter.OnResourceExecuting),
filter); filter.OnResourceExecuting(resourceExecutingContext); _diagnosticListener.AfterOnResourceExecuting(resourceExecutingContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IResourceFilter.OnResourceExecuting),
filter); if (resourceExecutingContext.Result != null)
{
_resourceExecutedContext = new ResourceExecutedContextSealed(resourceExecutingContext, _filters)
{
Canceled = true,
Result = _resourceExecutingContext.Result,
}; goto case State.ResourceShortCircuit;
} var task = InvokeNextResourceFilter();
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceSyncEnd;
return task;
} goto case State.ResourceSyncEnd;
} case State.ResourceSyncEnd:
{
Debug.Assert(state != null);
Debug.Assert(_resourceExecutingContext != null);
Debug.Assert(_resourceExecutedContext != null); var filter = (IResourceFilter)state;
var resourceExecutedContext = _resourceExecutedContext; _diagnosticListener.BeforeOnResourceExecuted(resourceExecutedContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IResourceFilter.OnResourceExecuted),
filter); filter.OnResourceExecuted(resourceExecutedContext); _diagnosticListener.AfterOnResourceExecuted(resourceExecutedContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.ResourceFilter,
nameof(IResourceFilter.OnResourceExecuted),
filter); goto case State.ResourceEnd;
} case State.ResourceShortCircuit:
{
Debug.Assert(state != null);
Debug.Assert(_resourceExecutingContext != null);
Debug.Assert(_resourceExecutedContext != null); _logger.ResourceFilterShortCircuited((IFilterMetadata)state); _result = _resourceExecutingContext.Result;
var task = InvokeAlwaysRunResultFilters();
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceEnd;
return task;
} goto case State.ResourceEnd;
} case State.ResourceInside:
{
goto case State.ExceptionBegin;
} case State.ExceptionBegin:
{
_cursor.Reset();
goto case State.ExceptionNext;
} case State.ExceptionNext:
{
var current = _cursor.GetNextFilter<IExceptionFilter, IAsyncExceptionFilter>();
if (current.FilterAsync != null)
{
state = current.FilterAsync;
goto case State.ExceptionAsyncBegin;
}
else if (current.Filter != null)
{
state = current.Filter;
goto case State.ExceptionSyncBegin;
}
else if (scope == Scope.Exception)
{
// All exception filters are on the stack already - so execute the 'inside'.
goto case State.ExceptionInside;
}
else
{
// There are no exception filters - so jump right to the action.
Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
goto case State.ActionBegin;
}
} case State.ExceptionAsyncBegin:
{
var task = InvokeNextExceptionFilterAsync();
if (!task.IsCompletedSuccessfully)
{
next = State.ExceptionAsyncResume;
return task;
} goto case State.ExceptionAsyncResume;
} case State.ExceptionAsyncResume:
{
Debug.Assert(state != null); var filter = (IAsyncExceptionFilter)state;
var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
// we'll call the filter. Otherwise there's nothing to do.
if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
{
_diagnosticListener.BeforeOnExceptionAsync(exceptionContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.ExceptionFilter,
nameof(IAsyncExceptionFilter.OnExceptionAsync),
filter); var task = filter.OnExceptionAsync(exceptionContext);
if (!task.IsCompletedSuccessfully)
{
next = State.ExceptionAsyncEnd;
return task;
} goto case State.ExceptionAsyncEnd;
} goto case State.ExceptionEnd;
} case State.ExceptionAsyncEnd:
{
Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null); var filter = (IAsyncExceptionFilter)state;
var exceptionContext = _exceptionContext; _diagnosticListener.AfterOnExceptionAsync(exceptionContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.ExceptionFilter,
nameof(IAsyncExceptionFilter.OnExceptionAsync),
filter); if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
{
// We don't need to do anything to trigger a short circuit. If there's another
// exception filter on the stack it will check the same set of conditions
// and then just skip itself.
_logger.ExceptionFilterShortCircuited(filter);
} goto case State.ExceptionEnd;
} case State.ExceptionSyncBegin:
{
var task = InvokeNextExceptionFilterAsync();
if (!task.IsCompletedSuccessfully)
{
next = State.ExceptionSyncEnd;
return task;
} goto case State.ExceptionSyncEnd;
} case State.ExceptionSyncEnd:
{
Debug.Assert(state != null); var filter = (IExceptionFilter)state;
var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
// we'll call the filter. Otherwise there's nothing to do.
if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
{
_diagnosticListener.BeforeOnException(exceptionContext, filter);
_logger.BeforeExecutingMethodOnFilter(
FilterTypeConstants.ExceptionFilter,
nameof(IExceptionFilter.OnException),
filter); filter.OnException(exceptionContext); _diagnosticListener.AfterOnException(exceptionContext, filter);
_logger.AfterExecutingMethodOnFilter(
FilterTypeConstants.ExceptionFilter,
nameof(IExceptionFilter.OnException),
filter); if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
{
// We don't need to do anything to trigger a short circuit. If there's another
// exception filter on the stack it will check the same set of conditions
// and then just skip itself.
_logger.ExceptionFilterShortCircuited(filter);
}
} goto case State.ExceptionEnd;
} case State.ExceptionInside:
{
goto case State.ActionBegin;
} case State.ExceptionHandled:
{
// We arrive in this state when an exception happened, but was handled by exception filters
// either by setting ExceptionHandled, or nulling out the Exception or setting a result
// on the ExceptionContext.
//
// We need to execute the result (if any) and then exit gracefully which unwinding Resource
// filters. Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null); if (_exceptionContext.Result == null)
{
_exceptionContext.Result = new EmptyResult();
} _result = _exceptionContext.Result; var task = InvokeAlwaysRunResultFilters();
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceInsideEnd;
return task;
} goto case State.ResourceInsideEnd;
} case State.ExceptionEnd:
{
var exceptionContext = _exceptionContext; if (scope == Scope.Exception)
{
isCompleted = true;
return Task.CompletedTask;
} if (exceptionContext != null)
{
if (exceptionContext.Result != null ||
exceptionContext.Exception == null ||
exceptionContext.ExceptionHandled)
{
goto case State.ExceptionHandled;
} Rethrow(exceptionContext);
Debug.Fail("unreachable");
} var task = InvokeResultFilters();
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceInsideEnd;
return task;
}
goto case State.ResourceInsideEnd;
} case State.ActionBegin:
{
var task = InvokeInnerFilterAsync();
if (!task.IsCompletedSuccessfully)
{
next = State.ActionEnd;
return task;
} goto case State.ActionEnd;
} case State.ActionEnd:
{
if (scope == Scope.Exception)
{
// If we're inside an exception filter, let's allow those filters to 'unwind' before
// the result.
isCompleted = true;
return Task.CompletedTask;
} Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
var task = InvokeResultFilters();
if (!task.IsCompletedSuccessfully)
{
next = State.ResourceInsideEnd;
return task;
}
goto case State.ResourceInsideEnd;
} case State.ResourceInsideEnd:
{
if (scope == Scope.Resource)
{
_resourceExecutedContext = new ResourceExecutedContextSealed(_actionContext, _filters)
{
Result = _result,
}; goto case State.ResourceEnd;
} goto case State.InvokeEnd;
} case State.ResourceEnd:
{
if (scope == Scope.Resource)
{
isCompleted = true;
return Task.CompletedTask;
} Debug.Assert(scope == Scope.Invoker);
Rethrow(_resourceExecutedContext!); goto case State.InvokeEnd;
} case State.InvokeEnd:
{
isCompleted = true;
return Task.CompletedTask;
} default:
throw new InvalidOperationException();
}
}

上面这段就是过滤器的核心代码了,其实很简单,就是在一个switch里面不停地根据条件跳转,下面是官网上不同过滤器的执行顺序图

那么过滤器和管道之间的执行顺序又是什么样的呢?直接看官方文档内的图:

其实从刚开始的单步调试我们就已经知道了,是在EndpointMiddleware管道内执行的,也就是说先执行了一些我们自己添加的中间件,到最后和Action交互的时候才会执行相关的过滤器

过滤器和管道的重要区别

  1. 中间件可以处理所有的请求,而过滤器只能针对到达EndpointMiddleware Api的请求进行处理

  2. 过滤器处理的是ActionExecutingContext、ResultExecutedContext等,而中间件处理的是HttpContext,相较于HttpContext,ActionExecutingContext拥有了更多的信息,比如执行的方法,对应的参数等

以异常处理来举例,我们可以在中间件的级别来处理异常,也可以在过滤器的级别来处理异常,那一般情况下我们应该怎么选择呢?

  1. 如果我们需要处理Asp.Net Core框架内部的错误,比如管道中处理出现了一些异常,我们需要使用中间件处理异常,并且位于管道的开始位置
  2. 如果我们只关心自己的业务代码是否出现异常,比如controller中的action抛出了异常,我们可以使用过滤器,这样有个好处就是我们更方便获取对应抛出异常方法的信息
  3. 如果我们又想处理Asp.Net Core框架内部的异常,又想处理业务逻辑的异常,那么我们可以两者都同时使用

过滤器执行顺序

过滤器的执行是有顺序的,官方文档提供了一个表格我们可以参考一下:

Sequence Filter scope Filter method
1 Global OnActionExecuting
2 Controller OnActionExecuting
3 Action OnActionExecuting
4 Action OnActionExecuted
5 Controller OnActionExecuted
6 Global OnActionExecuted

看得出来顺序是这样的:

  1. 全局级别
  2. Controller级别
  3. Action级别

返回时执行顺序相反,这个顺序也不是写死的,我们可以通过配置一个字段来设置顺序:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute :
Attribute, IActionFilter, IAsyncActionFilter, IResultFilter, IAsyncResultFilter, IOrderedFilter
{
/// <inheritdoc />
public int Order { get; set; }
}

实现了IOrderedFilter接口的过滤器都可以通过Order字段设置执行顺序,这个Order的默认值都是0,我们可以在自定义的过滤器上添加下面的代码来测试是否是这样的:

public int Level { get; set; } = 0;
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
Console.WriteLine($"OnActionExecuting {Order} {Level}");
}

在全局、Controller和Action级别都添加一个:

[MyActionFilter(Level = 100)]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{ public ValuesController()
{
}
[MyActionFilter(Level = 99)]
[HttpGet("test")]
public string Test()
{
return "123";
}
}

最后调用这个接口,输出的结果为:

OnActionExecuting 0 0
OnActionExecuting 0 100
OnActionExecuting 0 99

可以看出,默认的Order都是0,如果我们想要将某个过滤器在其它过滤器前执行,则可以手动设置Order的顺序,Order越小优先级越高

这里有个问题,如果我们设置某一个过滤器的执行顺序在全局和Controller之间,那我们也必须设置Controller级别的过滤器的Order属性,比如Action级别的过滤器Order为1,那么Controller级别的过滤器Orde必须大于1,如果涉及到很多个过滤器的话这里会比较复杂,而且不直观,所以一般情况下都不会对这个字段进行修改

参考

  1. https://stackoverflow.com/questions/42582758/asp-net-core-middleware-vs-filters

  2. https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0

  3. https://www.yogihosting.com/advanced-filters-topics-aspnet-core/#execution-order

  4. https://www.dotnettricks.com/learn/aspnetcore/mvc-core-filters-real-world-exmaple

  5. https://stackoverflow.com/questions/50887540/exceptionhandling-middleware-vs-filter-aspnetcore-webapi-2-1?noredirect=1&lq=1

  6. https://www.cnblogs.com/Bo-H/p/16584380.html

Asp-Net-Core-管道VS过滤器的更多相关文章

  1. ASP.NET CORE 管道模型及中间件使用解读

    说到ASP.NET CORE 管道模型不得不先来看看之前的ASP.NET 的管道模型,两者差异很大,.NET CORE 3.1 后完全重新设计了框架的底层,.net core 3.1 的管道模型更加灵 ...

  2. 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

    在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...

  3. ASP.NET Core管道深度剖析(4):管道是如何建立起来的?

    在<管道是如何处理HTTP请求的?>中,我们对ASP.NET Core的请求处理管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.这样一 ...

  4. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

  5. ASP.Net 管道模型 VS Asp.Net Core 管道 总结

    1 管道模型  1 Asp.Net Web Form管道 请求进入Asp.Net工作进程后,由进程创建HttpWorkRequest对象,封装此次请求有关的所有信息,然后进入HttpRuntime类进 ...

  6. ASP.NET Core管道深度剖析[共4篇]

    之所以称ASP.NET Core是一个Web开发平台,源于它具有一个极具扩展性的请求处理管道,我们可以通过这个管道的定制来满足各种场景下的HTTP处理需求.ASP. NET Core应用的很多特性,比 ...

  7. ASP.NET Core管道深度剖析(3):管道是如何处理HTTP请求的?

    我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很少人能够地把它弄清楚.为了让读者 ...

  8. ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求

    之所以称ASP.NET Core是一个Web开发平台,源于它具有一个极具扩展性的请求处理管道,我们可以通过这个管道的定制来满足各种场景下的HTTP处理需求.ASP. NET Core应用的很多特性,比 ...

  9. 【WPF】【UWP】借鉴 asp.net core 管道处理模型打造图片缓存控件 ImageEx

    在 Web 开发中,img 标签用来呈现图片,而且一般来说,浏览器是会对这些图片进行缓存的. 比如访问百度,我们可以发现,图片.脚本这种都是从缓存(内存缓存/磁盘缓存)中加载的,而不是再去访问一次百度 ...

  10. ASP.NET Core管道深度剖析

    ASP.NET管道 以IIS 6.0为例,在工作进程w3wp.exe中,利用Aspnet_ispai.dll加载.NET运行时(如果.NET运行时尚未加载).IIS 6引入了应用程序池的概念,一个工作 ...

随机推荐

  1. Kubeadm部署高可用K8S集群

    一 基础环境 1.1 资源 节点名称 ip地址 VIP 192.168.12.150 master01 192.168.12.48 master02 192.168.12.242 master03 1 ...

  2. 基于Seq2Seq和注意力机制的句子翻译

    Seq2Seq(Attention) 目录 Seq2Seq(Attention) 1.理论 1.1 机器翻译 1.1.1 模型输出结果处理 1.1.2 BLEU得分 1.2 注意力模型 1.2.1 A ...

  3. c语言KMP匹配算法与字符串替换算法

    一.字符串匹配算法 (1)传统匹配算法BF int Index_BF(char* S, char* T){ int i=1,j=1; while(i<=strlen(S) && ...

  4. win10+ubuntu双系统的坑

    1.把U盘里\EFI\BOOT\grubx64.efi文件重命名为mmx64.efi,避免系统提示缺少文件而退出安装: 2.如果电脑显卡为N卡,则在install Ubuntu时,按e进入编辑,在qu ...

  5. SpringBoot报错: No identifier specified for entity: XXX.XXX.XXX.XXX

    今天练习的时候报错说是 : 没有为实体指定标识符 仔细看了实体类才发现忘记写了一些注解 用JPA写实体类时一些注解是必须的 @entity  标名本类是实体类 @table(name="表名 ...

  6. STF的DOCKER搭建

    OPENSTF OpenSTF(Smartphone Test Farm)是一个web端移动设备管理平台,可以从浏览器端远程调试.远程管理设备.其实有点类似于我们现在很火热的云测平台,如:testin ...

  7. Windows骚操作

    电脑常用的快捷键 键盘功能健:Tab.Shift.Ctrl.Alt.Windows.Enter.空格.上下左右健.CapsLock(大小写转换).NumLock(对小键盘控制开/关) 键盘快捷键:全选 ...

  8. java学习之SpringMVC拦截器开发

    0x00前言 springmvc的拦截器类似于Selvet的Filter,但是所属的操作又不一样 Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理,也就是增强 ...

  9. git ignore忽略规则

    目录 Git 忽略文件提交的方法 Git 忽略规则 Git 忽略规则优先级 Git 忽略规则匹配语法 常用匹配示例 多级目录忽略规则设置 .gitignore规则不生效 参考文章 Git 忽略文件提交 ...

  10. 【Virt.Contest】CF1321(div.2)

    第一次打虚拟赛. CF 传送门 T1:Contest for Robots 统计 \(r[i]=1\) 且 \(b[i]=0\) 的位数 \(t1\) 和 \(r[i]=0\) 且 \(b[i]=1\ ...