我们知道NET Core
应用程序的核心配置在项目中的Startup.cs
文件.
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
return services.AddAbp<WebModule>(//...);
}
public void Configure(IApplicationBuilder app)
{
app.UseAbp(//...);
//...
}
}
public void ConfigureServices(IServiceCollection services)
方法主要用于将服务添加到DI
容器中并做一些配置.public void Configure(IApplicationBuilder app)
配置请求管道.
Abp
框架使用自己的DI容器(主要为了兼容之前的NET Framework版本和使用一些高级特性,如拦截器),所以我们在ConfigureServices
方法的底部会看到return services.AddAbp(//...)
这时候ConfigureServices
的方法返回值也变成了IServiceProvider
(关于这点请参考:默认服务容器替换)
框架与ASPNET Core
深度集成的地方就在AddAbp
方法和UseAbp
的内部:
public static class AbpServiceCollectionExtensions
{
/// <summary>
/// Integrates ABP to AspNet Core.
/// </summary>
/// <typeparam name="TStartupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</typeparam>
/// <param name="services">Services.</param>
/// <param name="optionsAction">An action to get/modify options</param>
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);
ConfigureAspNetCore(services, abpBootstrapper.IocManager);
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
//See https://github.com/aspnet/Mvc/issues/3936 to know why we added these services.
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
//Use DI to create controllers
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
//Use DI to create view components
services.Replace(ServiceDescriptor.Singleton<IViewComponentActivator, ServiceBasedViewComponentActivator>());
//Change anti forgery filters (to work proper with non-browser clients)
services.Replace(ServiceDescriptor.Transient<AutoValidateAntiforgeryTokenAuthorizationFilter, AbpAutoValidateAntiforgeryTokenAuthorizationFilter>());
services.Replace(ServiceDescriptor.Transient<ValidateAntiforgeryTokenAuthorizationFilter, AbpValidateAntiforgeryTokenAuthorizationFilter>());
//Add feature providers
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver));
//Configure JSON serializer
services.Configure<MvcJsonOptions>(jsonOptions =>
{
jsonOptions.SerializerSettings.ContractResolver = new AbpMvcContractResolver(iocResolver)
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
//Configure MVC
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
});
//Configure Razor
services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<RazorViewEngineOptions>>(
new ConfigureOptions<RazorViewEngineOptions>(
(options) =>
{
options.FileProviders.Add(new EmbeddedResourceViewFileProvider(iocResolver));
}
)
)
);
}
private static AbpBootstrapper AddAbpBootstrapper<TStartupModule>(IServiceCollection services, Action<AbpBootstrapperOptions> optionsAction)
where TStartupModule : AbpModule
{
var abpBootstrapper = AbpBootstrapper.Create<TStartupModule>(optionsAction);
services.AddSingleton(abpBootstrapper);
return abpBootstrapper;
}
}
AbpBootstrapper
作为Abp
核心的引导类,负责初始化Abp
系统,加载插件,配置Abp
和模块以及启动模块(PreInitialize
,Initialize
, PostInitialize
). 这个引导类在console应用程序中同样可以启动Abp
系统.
接下来Abp
配置和替换了多个MVC
的组件,如IControllerActivator,IViewComponentActivator,MvcJsonOptions,MvcOptions
更多的配置在mvcOptions.AddAbp(services)
方法下:
internal static class
{
public static void AddAbp(this MvcOptions options, IServiceCollection services)
{
AddConventions(options, services);
AddFilters(options);
AddModelBinders(options);
}
private static void AddConventions(MvcOptions options, IServiceCollection services)
{
options.Conventions.Add(new AbpAppServiceConvention(services));
}
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));
}
private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
}
}
AbpAppServiceConvention
主要用于配置动态API,这个后面会有一章单独说明.
AbpAuthorizationFilter
AbpAuditActionFilter
AbpValidationActionFilter
AbpUowActionFilter
AbpExceptionFilter
AbpResultFilter
依次添加6个MVC
过滤器分别实现授权,审计,参数验证,工作单元,异常处理以及返回内容的包装.(请注意过滤器顺序很重要)
AbpDateTimeModelBinderProvider
用作DateTime
的统一时区处理.
接下来我们再看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);
if (options.UseCastleLoggerFactory)
{
app.UseCastleLoggerFactory();
}
InitializeAbp(app);
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
}
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
}
public static void UseEmbeddedFiles(this IApplicationBuilder app)
{
app.UseStaticFiles(
new StaticFileOptions
{
FileProvider = new EmbeddedResourceFileProvider(
app.ApplicationServices.GetRequiredService<IIocResolver>()
)
}
);
}
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize();
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
public static void UseCastleLoggerFactory(this IApplicationBuilder app)
{
var castleLoggerFactory = app.ApplicationServices.GetService<Castle.Core.Logging.ILoggerFactory>();
if (castleLoggerFactory == null)
{
return;
}
app.ApplicationServices
.GetRequiredService<ILoggerFactory>()
.AddCastleLogger(castleLoggerFactory);
}
public static void UseAbpRequestLocalization(this IApplicationBuilder app, Action<RequestLocalizationOptions> optionsAction = null)
{
var iocResolver = app.ApplicationServices.GetRequiredService<IIocResolver>();
using (var languageManager = iocResolver.ResolveAsDisposable<ILanguageManager>())
{
var supportedCultures = languageManager.Object
.GetLanguages()
.Select(l => CultureInfo.GetCultureInfo(l.Name))
.ToArray();
var options = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
var userProvider = new AbpUserRequestCultureProvider();
//0: QueryStringRequestCultureProvider
options.RequestCultureProviders.Insert(1, userProvider);
options.RequestCultureProviders.Insert(2, new AbpLocalizationHeaderRequestCultureProvider());
//3: CookieRequestCultureProvider
options.RequestCultureProviders.Insert(4, new AbpDefaultRequestCultureProvider());
//5: AcceptLanguageHeaderRequestCultureProvider
optionsAction?.Invoke(options);
userProvider.CookieProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
userProvider.HeaderProvider = options.RequestCultureProviders.OfType<AbpLocalizationHeaderRequestCultureProvider>().FirstOrDefault();
app.UseRequestLocalization(options);
}
}
public static void UseAbpSecurityHeaders(this IApplicationBuilder app)
{
app.UseMiddleware<AbpSecurityHeadersMiddleware>();
}
}
核心InitializeAbp(app)
,使用上文之前提到AbpBootstrapper
引导启动Abp
系统.(我们知道Abp模块有个Shutdown()
方法,Abp
使用IApplicationLifetime
接口捕获应用程序事件实现模块的Shoutdown
)
之后按需启动几个中间件,如: RequestLocalization
,SecurityHeaders
等(这些小组件和之前的过滤器后面我会分别详细介绍,这里就不深入讲解)
至此Abp
已经把框架的相关组件集成到了MVC
中.接下来程序启动.请求被各种中间件处理(MVC中间件会调用过滤器).
Abp中文网:https://cn.abp.io/
Abp交流群:735901849(纯技术交流,无广告,不卖课)