ASP.NET Core应用的背后是一个由Server和Middleware构成的管道,Server实现针对请求的监听、接收和响应,而注册的Middleware则负责对请求进行处理
1、AspNetCore模块
Microsoft:包括ApplicationBuilder的扩展,如UseAuditing,UseUnitOfWork(UseAbpExceptionHandling),UseCorrelationId,UseAbpRequestLocalization
UseVirtualFiles ,CorsPolicyBuilder.WithExposedHeaders("_AbpErrorFormat")
Volo:
配置审计模块,AbpAuditingOptions增加AspNetCoreAuditLogContributor。作用?
[DependsOn( typeof(AbpAuditingModule), typeof(AbpSecurityModule), typeof(AbpVirtualFileSystemModule), typeof(AbpUnitOfWorkModule), typeof(AbpHttpModule), typeof(AbpAuthorizationModule), typeof(AbpDddDomainModule), //TODO: Can we remove this? typeof(AbpLocalizationModule), typeof(AbpUiModule), //TODO: Can we remove this? typeof(AbpValidationModule) )] public class AbpAspNetCoreModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.AddConfiguration(); } public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpAuditingOptions>(options => { options.Contributors.Add(new AspNetCoreAuditLogContributor()); }); AddAspNetServices(context.Services); context.Services.AddObjectAccessor<IApplicationBuilder>(); } private static void AddAspNetServices(IServiceCollection services) { services.AddHttpContextAccessor(); } }
2、审计
添加审记中间件,保存审记记录
public async Task Invoke(HttpContext httpContext) { if (!ShouldWriteAuditLog(httpContext)) { await _next(httpContext); return; } using (var scope = _auditingManager.BeginScope()) { try { await _next(httpContext); } finally { await scope.SaveAsync(); } } } private bool ShouldWriteAuditLog(HttpContext httpContext) { if (!Options.IsEnabled) { return false; } if (!Options.IsEnabledForAnonymousUsers && !CurrentUser.IsAuthenticated) { return false; } if (!Options.IsEnabledForGetRequests && string.Equals(httpContext.Request.Method, HttpMethods.Get, StringComparison.OrdinalIgnoreCase)) { return false; } return true; }
AuditLogContributor的实现AspNetCoreAuditLogContributor,AuditLogContributionContext下的AuditLogInfo的HttpMethod,Url,ClientIpAddress,BrowserInfo
3、依赖注入
IHybridServiceScopeFactory的替换实现服务
public virtual IServiceScope CreateScope() { var httpContext = HttpContextAccessor.HttpContext; if (httpContext == null) { return ServiceScopeFactory.CreateScope(); } return new NonDisposedHttpContextServiceScope(httpContext.RequestServices); } protected class NonDisposedHttpContextServiceScope : IServiceScope { public IServiceProvider ServiceProvider { get; } public NonDisposedHttpContextServiceScope(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; } public void Dispose() { } }
3、异常处理
异常处理中间件
4、安全
HttpContextCurrentPrincipalAccessor;
ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal;
5、线程
HttpContextCancellationTokenProvider
public CancellationToken Token => _httpContextAccessor.HttpContext?.RequestAborted ?? CancellationToken.None;
6、Trace,中间件AbpCorrelationIdMiddleware
7、工作单元,中间件AbpUnitOfWorkMiddleware
8、VirtualFileSystem;AspNetCoreContentOptions,IFileProvider等服务
2、AspNetCore.Mvc
建立在ASP.NET Core的所有的开发框架都是通过注册到管道中的某一个或者多个Middleware实现的。针对MVC的Middleware实现了路由、Controller的激活、Action方法的执行以及View的呈现
1、Routing路由
路由中间件主要包含以下几个部分:URL 匹配 URL 生成 IRouter 接口 路由模板 模板约束
首先传入请求会到注册的 RouterMiddleware 中间件,然后它 RouteAsync 按顺序调用每个路由上的方法。当一个请求到来的时候,IRouter实例选择是否处理已经设置到 RouteContext
Handler
上的一个非空 RequestDelegate。如果Route已经为该请求设置处理程序,则路由处理会中止并且开始调用设置的Hanlder处理程序以处理请求。如果当前请求尝试了所有路由都没有找到处理程序的话,则调用next,将请求交给管道中的下一个中间件。
关于路由模板和参数约束源码处理流程就不一一说了,有兴趣可以直接看下源码。
在 ASP.NET Core 中 HttpContext 是一个抽象类,位于 Microsoft.AspNetCore.Http
命名空间下。它有一个默认的实现叫 DefaultHttpContext
位于 Microsoft.AspNetCore.Http 程序集。
ASP.NET Core MVC 源码程序主要包含几部分组成:
- Mvc.Core :源码的核心实现,包含认证,过滤,模型绑定,路由等等...
- Mvc.Razor:Razor视图的拓展实现,模板引擎等,核心实现在Rozor那个项目。
- Mvc.TagHelper:Razor中 TagHelper的主要实现。
- Mvc.ViewFeatures:Razor中视图组件的渲染等。
在MVC中所有的注入都是使用 TryAddXXX
的形式,也就是如果容器中已经有相关服务的话,将不会添加新注册的服务
AddMvc 的返回值 IMvcBuilder
, IMvcBuilder
是一个针对 IServiceCollection 包装的一个接口,除了IServiceCollection之外,还有一个 ApplicationPartManager
。那么它是干嘛的呢?从命名来看 ApplicationPartManager
是用来管理 ApplicationPart
的
目前 MVC 框架针对 ApplicationPart 的默认实现只有 AssemblyPart,当然你可以根据需要进行扩展。
1、 程序集中定义的 Action 是怎么找到的?
其实就是在 MVC 框架启动的时候,首先会把 Assembly 程序集转换为 ApplicationPart 添加到 ApplicationPartManager 对象列表中,才能执行后续的任务,因为要从这些程序集中查找 Controller,那么从这个特性我们可以延伸到, 利用此功能,我们可以从 Web 层剥离 Controller 到其他程序集中。
要想找到程序定义的所有 Action,那么首先需要找到 Controller,在上一篇文章中我们已经知道了有一个 MVC 程序用来管理 AssemblyPart 的东西叫 ApplicationPartManager ,它的里面存储了所有 MVC 框架在启动的时候加载的所有程序集,那么我们可以从这个程序集中找到需要的 Controller。下面这个流程图显示了查找Controller 的流程:
public interface IRouter
{
Task RouteAsync(RouteContext context);
VirtualPathData GetVirtualPath(VirtualPathContext context);
}
这个接口主要干两件事情,第一件是根据路由上下文来进行路由处理,第二件是根据虚拟路径上下文获取 VirtualPathData
。
默认处理程序: MvcRouteHandler
,用来处理约定的 Action。
注解处理程序: MvcAttributeRouteHandler
,用来处理注解(Attribute)路由。
Antiforgery
用Antiforgery防御CSRF,Microsoft.AspNetCore.Antiforgery软件包已作为Microsoft.AspNetCore.Mvc的依赖项包含在内。 同样,通过在Startup.ConfigureServices()方法中调用services.addMvc(),所需的Antiforgery服务会自动在DI容器中注册。
TokenCookieName = "XSRF-TOKEN"
TokenHeaderName = "X-XSRF-TOKEN";
ApiExploring
[Route("api/abp/api-definition")] =》IApiDescriptionModelProvider
AspNetCoreApiDescriptionModelProvider
{
"modules":{
app:{
"rootPath":"app",
controllers:{
Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController:{
controllerName:
typeAsString:
interfaces:
actions:{
GetAsync:{
uniqueName,name,httpMethod,url,supportedVersions,parametersOnMethod,parameters,returnValue
}
}
}}}}}
3
public interface ICurrentPrincipalAccessor { ClaimsPrincipal Principal { get; } } }
Volo.Abp.AspNetCore.Security.Claims
public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency { public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal; } public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor { public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal; private readonly IHttpContextAccessor _httpContextAccessor; public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } }