<深入理解Abp> 程序启动 - 一切的开始

我们知道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(纯技术交流,无广告,不卖课)

猜你喜欢

转载自www.cnblogs.com/realmaliming/p/10050018.html