[Abp源码分析]⼗七、ASP.NETCore集成
0. 简介
整个 Abp 框架最为核⼼的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了。虽然 Abp 本⾝是可以⽤于控制台程序的,不过那样的话 Abp 就基本没什么⽤,还是需要集合ASP.NET Core 才能发挥它真正的作⽤。
在 Abp.AspNetCore 库⾥⾯,Abp 通过 WindsorRegistrationHelper.CreateServiceProvider() 接管了 ASP.NET Core ⾃带的 Ioc 容器。除此之外,还针对 Controller 的⽣成规则也进⾏了替换,以便实现 Dynamic API 功能。
总的来说,整个 Abp 框架与 ASP.NET Core 集成的功能都放在这个库⾥⾯的,所以说这个库还是相当重要的。这个项⽬⼜依赖于 Abp.Web.Common 库,这个库是存放了很多公⽤⽅法或者⼯具类的,后⾯也会有讲述。
1. 启动流程
⾸先在 Abp.AspNetCore 库⾥⾯,Abp 提供了两个扩展⽅法。
第⼀个则是 AddAbp<TStartupModule>() ⽅法。
该⽅法是 IServiceCollection 的扩展⽅法,⽤于在 ASP.NET Core 项⽬⾥⾯的 Startup 的 ConfigureService() 进⾏配置。通过该⽅法,Abp 会接管默认的 DI 框架,改为使⽤Castle Windsor,并且进⾏⼀些 MVC 相关的配置。
第⼆个则是 UseAbp() ⽅法。
该⽅法是 IApplicationBuilder 的扩展⽅法,⽤于 Startup 类⾥⾯的 Configure() 配置。通过该⽅法,Abp 会执⾏⼀系列初始化操作,在这个时候 Abp 框架才算是真正地启动了起来。
下⾯则是常规的⽤法:
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
return services.AddAbp<AspNetCoreAppModule>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
app.UseAbp();
}
}
基本上可以说,UseAbp() 就是整个 Abp 框架的⼊⼝点,负责调⽤ AbpBootstrapper 来初始化整个 Abp 项⽬并加载各个模块。
2. 代码分析
在 Abp.AspNetCore 库中,基本上都是针对 ASP.NET Core 的⼀些相关组件进⾏替换。⼤体上有过滤器、控制器、多语⾔、动态 API、CSRF 防御组件这⼏⼤块东西,下⾯我们先按照 AddAbp() ⽅法与 UseAbp() ⽅法内部注⼊的顺序依次进⾏讲解。
⾸先我们讲解⼀下 AddAbp() ⽅法与 UseAbp() ⽅法的内部做了什么操作吧。
2.1 初始化操作
2.1.1 组件替换与注册
我们⾸先查看 AddAbp() ⽅法,该⽅法存在于 AbpServiceCollectionExtensions.cs ⽂件之中。
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsActi on = null)
where TStartupModule : AbpModule
{
// 传⼊启动模块,构建 AddAbpBootstrapper 对象,并将其注⼊到 Ioc 容器当中
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);
// 配置 ASP.NET Core 相关的东西
ConfigureAspNetCore(services, abpBootstrapper.IocManager);
// 返回⼀个新的 IServiceProvider ⽤于替换⾃带的 DI 框架
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
该⽅法作为 IServiceCollection 的扩展⽅法存在,⽅便⽤户进⾏使⽤,⽽在 ConfigureAspNetCore() ⽅法之中,主要针对 ASP.NET Core 进⾏了相关的配置。
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
// ⼿动注⼊ HTTPContext 访问器等
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
// 替换掉默认的控制器构造类,改⽤ DI 框架负责控制器的创建
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
// 替换掉默认的视图组件构造类,改⽤ DI 框架负责视图组件的创建
services.Replace(ServiceDescriptor.Singleton<IViewComponentActivator, ServiceBasedViewComponentActivator>());
// 替换掉默认的 Antiforgery 类 (主要⽤于⾮浏览器的客户端进⾏调⽤)
services.Replace(ServiceDescriptor.Transient<AutoValidateAntiforgeryTokenAuthorizationFilter, AbpAutoValidateAntiforgeryTokenAuthorizationFilt er>());
services.Replace(ServiceDescriptor.Transient<ValidateAntiforgeryTokenAuthorizationFilter, AbpValid
ateAntiforgeryTokenAuthorizationFilter>()); // 添加 Feature Provider,⽤于判断某个类型是否为控制器
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver));
// 配置 JSON 序列化
services.Configure<MvcJsonOptions>(jsonOptions =>
{
jsonOptions.SerializerSettings.ContractResolver = new AbpMvcContractResolver(iocResolver)
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
/
/ 配置 MVC 相关的东西,包括控制器⽣成和过滤器绑定
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
});
// 配置 Razor 相关参数
services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<RazorViewEngineOptions>>(
new ConfigureOptions<RazorViewEngineOptions>(
(options) =>
{
options.FileProviders.Add(new EmbeddedResourceViewFileProvider(iocResolver));
}
)
)
);
}
之后来到 mvcOptions.AddAbp(services); 所指向的类型,可以看到如下代码:
internal static class AbpMvcOptionsExtensions
{
public static void AddAbp(this MvcOptions options, IServiceCollection services)
{
AddConventions(options, services);
AddFilters(options);
AddModelBinders(options);
}
// 添加 Abp 定义的 Controller 约定,主要⽤于配置 Action ⽅法的 HttpMethod 与路由
private static void AddConventions(MvcOptions options, IServiceCollection services)
{
options.Conventions.Add(new AbpAppServiceConvention(services));asp网页源码
}
// 添加各种过滤器
private static void AddFilters(MvcOptions options)
{
options.Filters.AddService(typeof(AbpAuthorizationFilter));
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter));
options.Filters.AddService(typeof(AbpResultFilter));
}
// 添加 Abp 定义的模型绑定器,主要是为了处理时间类型
private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
}
}
这⾥⾯所做的⼯作基本上都是进⾏⼀些组件的注⼊与替换操作。
2.1.2 Abp 框架加载与初始化
Abp 框架的初始化与加载则是在 UseAbp() ⽅法⾥⾯进⾏的,⾸先看它的两个重载⽅法。
public static class AbpApplicationBuilderExtensions
{
public static void UseAbp(this IApplicationBuilder app)
{
app.UseAbp(null);
}
public static void UseAbp([NotNull] this IApplicationBuilder app, Action<AbpApplicationBuilderOptions> optionsAction)
{
Check.NotNull(app, nameof(app));
var options = new AbpApplicationBuilderOptions();
// 获取⽤户传⼊的配置操作
optionsAction?.Invoke(options);
// 是否启⽤ Castle 的⽇志⼯⼚
if (options.UseCastleLoggerFactory)
{
app.UseCastleLoggerFactory();
}
// Abp 框架开始加载并初始化
InitializeAbp(app);
// 是否根据请求进⾏本地化处理
if (options.UseAbpRequestLocalization)
{
//TODO: 这个中间件应该放在授权中间件之后
app.UseAbpRequestLocalization();
}
// 是否使⽤安全头
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
}
// ... 其他代码
}
在 UseAbp() 当中你需要注意的是 InitializeAbp(app); ⽅法。该⽅法在调⽤的时候,Abp 才会真正开始地进⾏初始化。在这个时候,Abp 会遍历所有项⽬并且执⾏它们的模块的三个⽣命周期⽅法。当所有模块都被调⽤过之后,Abp 框架就已经准备就绪了。
private static void InitializeAbp(IApplicationBuilder app)
{
// 使⽤ IApplicationBuilder 从 IServiceCollection 中获取之前 AddAbp() 所注⼊的 AbpBootstrapper 对
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
// 调⽤ AbpBootstrapper 的初始化⽅法,加载所有模块
abpBootstrapper.Initialize();
// 绑定 ASP.NET Core 的⽣命周期,当⽹站关闭时,调⽤ AbpBootstrapper 对象的 Dispose() ⽅法
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
2.2 AbpAspNetCoreModule 模块
如果说要了解 Abp 某⼀个库的话,第⼀步肯定是阅读该库提供的模块类型。因为不管是哪⼀个库,都会有⼀个模块进⾏库的基本配置与初始化动作,⽽且肯定是这个库第⼀个被Abp 框架所调⽤到的类型。
⾸先我们按照模块的⽣命周期来阅读模块的源代码,下⾯是模块的预加载 (PreInitialize())⽅法:
[DependsOn(typeof(AbpWebCommonModule))]
public class AbpAspNetCoreModule : AbpModule
{
public override void PreInitialize()
{
// 添加⼀个新的注册规约,⽤于批量注册视图组件
IocManager.AddConventionalRegistrar(new AbpAspNetCoreConventionalRegistrar());
IocManager.Register<IAbpAspNetCoreConfiguration, AbpAspNetCoreConfiguration>();
Configuration.ReplaceService<IPrincipalAccessor, AspNetCorePrincipalAccessor>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IAbpAntiForgeryManager, AbpAspNetCoreAntiForgeryManager>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IClientInfoProvider, HttpContextClientInfoProvider>(DependencyLifeStyle.Transient);
Configuration.Modules.AbpAspNetCore().FormBodyBindingIgnoredTypes.Add(typeof(IFormFile));
Configuration.MultiTenancy.Resolvers.Add<DomainTenantResolveContributor>();
Configuration.MultiTenancy.Resolvers.Add<HttpHeaderTenantResolveContributor>();
Configuration.MultiTenancy.Resolvers.Add<HttpCookieTenantResolveContributor>();
}
// ... 其他代码
}
可以看到在预加载⽅法内部,该模块通过 ReplaceService 替换了许多接⼝实现,也有很多注册了许多
组件,这其中就包括模块的配置类 IAbpAspNetCoreConfiguration 。[DependsOn(typeof(AbpWebCommonModule))]
public class AbpAspNetCoreModule : AbpModule
{
// ... 其他代码
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(AbpAspNetCoreModule).GetAssembly());
}
// ... 其他代码
}
初始化⽅法也更加简洁,则是通过 IocManager 提供的程序集扫描注册来批量注册⼀些组件。这⾥执⾏了该⽅法之后,会调⽤ BasicConventionalRegistrar 与AbpAspNetCoreConventionalRegistrar 这两个注册器来批量注册符合规则的组件。
[DependsOn(typeof(AbpWebCommonModule))]
public class AbpAspNetCoreModule : AbpModule
{
// ... 其他代码
public override void PostInitialize()
{
AddApplicationParts();
ConfigureAntiforgery();
}
private void AddApplicationParts()
{
// 获得当前库的配置类
var configuration = IocManager.Resolve<AbpAspNetCoreConfiguration>();
// 获得 ApplicationPart 管理器,⽤于发现指定程序集的应⽤服务,使其作为控制器进⾏初始化
var partManager = IocManager.Resolve<ApplicationPartManager>();
// 获得模块管理器,⽤于插件模块的加载
var moduleManager = IocManager.Resolve<IAbpModuleManager>();
// 获得控制器所在的程序集集合
var controllerAssemblies = configuration.ControllerAssemblySettings.Select(s => s.Assembly).Distinct();
foreach (var controllerAssembly in controllerAssemblies)
{
// ⽤程序集构造 AssemblyPart ,以便后⾯通过 AbpAppServiceControllerFeatureProvider 判断哪些类型是控制器
partManager.ApplicationParts.Add(new AssemblyPart(controllerAssembly));
}
// 从插件的程序集
var plugInAssemblies = moduleManager.Modules.Where(m => m.IsLoadedAsPlugIn).Select(m => m.Assembly).Distinct();
foreach (var plugInAssembly in plugInAssemblies)
{
partManager.ApplicationParts.Add(new AssemblyPart(plugInAssembly));
}
}
// 配置安全相关设置
private void ConfigureAntiforgery()
{
IocManager.Using<IOptions<AntiforgeryOptions>>(optionsAccessor =>
{
optionsAccessor.Value.HeaderName = Configuration.Modules.AbpWebCommon().AntiForgery.TokenHeaderName;
});
}
}
该模块的第三个⽣命周期⽅法主要是为了提供控制器所在的程序集,以便 ASP.NET Core MVC 进⾏控制器构造,其实这⾥仅仅是添加程序集的,⽽程序集有那么多类型,那么MVC 是如何判断哪些类型是控制器类型的呢?这个问题在下⾯⼀节进⾏解析。
2.3 控制器与动态 API
接着上⼀节的疑问,那么 MVC 所需要的控制器从哪⼉来呢?其实是通过在 AddAbp() 所添加的 AbpAppServiceControllerFeatureProvider 实现的。
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
// ... 其他代码
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver));
// ... 其他代码
}
下⾯我们分析⼀下该类型的内部构造是怎样的,⾸先看⼀下它的定义与构造器:
public class AbpAppServiceControllerFeatureProvider : ControllerFeatureProvider
{
private readonly IIocResolver _iocResolver;
public AbpAppServiceControllerFeatureProvider(IIocResolver iocResolver)
{
_iocResolver = iocResolver;
}
// ... 其他代码
}
类型定义都⽐较简单,继承⾃ ControllerFeatureProvider ,然后在构造函数传⼊了⼀个解析器。在该类型内部,重写了⽗类的⼀个 IsController() ⽅法,这个⽅法会传⼊⼀个TypeInfo 对象。其实你看到这⾥应该就明⽩了,之前在模块当中添加的程序集,最终会被 MVC 解析出所有类型然后调⽤这个 Provider 来判断哪些类型是控制器。
如果该类型是控制器的话,则返回 True,不是控制器则返回 False。
public class AbpAppServiceControllerFeatureProvider : ControllerFeatureProvider
{
// ... 其他代码
protected override bool IsController(TypeInfo typeInfo)
{
// 获得 Type 对象
var type = typeInfo.AsType();
/
/ 判断传⼊的类型是否继承⾃ IApplicationService 接⼝,并且不是泛型类型、不是抽象类型、访问级别为 public
if (!typeof(IApplicationService).IsAssignableFrom(type) ||
!typeInfo.IsPublic || typeInfo.IsAbstract || typeInfo.IsGenericType)
{
// 不满⾜上述条件则说明这个类型不能作为⼀个控制器
return false;
}
// 获取类型上⾯是否标注有 RemoteServiceAttribute 特性。
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(typeInfo);
// 如果有该特性,并且在特性内部的 IsEnabled 为 False 则该类型不能作为⼀个控制器
if (remoteServiceAttr != null && !remoteServiceAttr.IsEnabledFor(type))
{
return false;
}
// 从模块配置当中取得⼀个 Func 委托,该委托⽤于指定某些特性类型是否为⼀个控制器
var configuration = _iocResolver.Resolve<AbpAspNetCoreConfiguration>().ControllerAssemblySettings.GetSettingOrNull(type);
return configuration != null && configuration.TypePredicate(type);
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。