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. 过滤器处理的是ActionExecutingContextResultExecutedContext等,而中间件处理的是HttpContext,相较于HttpContextActionExecutingContext拥有了更多的信息,比如执行的方法,对应的参数等

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

  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. Linux家族谱系

    I II III VI unix linux Redhat Centos   Debian Ubuntu   SUSE   Android   BSD freeBSD NetBSD openBSD   ...

  2. JSON parse error: Cannot deserialize value of type `java.lang.Integer` from Boolean value

    问题原因所在:前端Vue传输的数据字段类型和后端实体类字段不一致. 我的实体类字段是int类型.前端传输的数据是布尔类型. 文章目录 1.后端方法 2.实体类字段 2.前端传输的数据 1.后端方法 @ ...

  3. 齐博x1云市场注意事项

    安装云市场应用注意事项 大到频道,小到插件甚至钩子及风格都可以在线安装,在线升级. 但是有一个大家务必注意的地方,就是重装系统后,再安装有可能导致重复收费. 这个问题是可以解决的.当然如果不是重装系统 ...

  4. 一、Go语言开篇介绍

    Go语言开篇介绍 Go语言 是Google公司 在2007开发一种静态强类型.编译型语言,并在 2009 年正式对外发布. Go语言以其近C的执行性能和近解析型语言的开发效率,以及近乎于完美的编译速度 ...

  5. 四、redis数据类型

    四.redis数据类型 redis可以理解成一个全局的大字典,key就是数据的唯一标识符.根据key对应的值不同,可以划分成5个基本数据类型. 1. string类型: 字符串类型,是 Redis 中 ...

  6. .NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

    微软终于追上了? 图片来自 Glenn Carstens-Peters Unsplash 欢迎来到.NET性能系列的另一章.这个系列的特点是对.NET世界中许多不同的主题进行研究.基准和比较.正如标题 ...

  7. Python基础之面向对象:1、面向对象及编程思想

    一.人狗大战 1.需求 用代码模拟人.狗打架的小游戏 人和狗种类不同,因此双方的属性各不相同 推导一: 人和狗各有不同属性 使用字典方式储存属性较为方便,并可储存多种属性 # 1.在字典内储存'人'属 ...

  8. VUE学习2

    目录分析 public目录 index.html是起始的html文件 # 这是关键 <div id="app"></div> src目录 main.js是V ...

  9. Oracle:ORA-00911: invalid character解决办法

    问题记录:用jmeter执行sql语句,报错:ORA-00911: invalid character. 解决方法:sql语句末尾";"导致,去掉即可解决. 过程记录: 使用jme ...

  10. IO多路复用的理解/演变过程

    目录 阻塞IO 非阻塞 IO select epoll 总结一下. 阻塞IO 服务端为了处理客户端的连接和请求的数据,写了如下代码. listenfd = socket(); // 打开一个网络通信端 ...