欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-07 19:13:14
我们知道 应用程序的核心配置在项目中的 " " 文件. 方法主要用于将服务添加到 容器中并做一些配置. 配置请求管道. 框架使用自己的DI容器\(主要为了兼容之前的NET Framework版本和使用一些高级特性,如拦截器\),所以我们在 方法的底部会看到 这时候 的方法返回值也变成了 \(关于这点 ......

我们知道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> 程序启动 - 一切的开始

至此abp已经把框架的相关组件集成到了mvc中.接下来程序启动.请求被各种中间件处理(mvc中间件会调用过滤器).

abp中文网:
abp交流群:735901849(纯技术交流,无广告,不卖课)