.net core WebApplication.CreateBuilder(args)时发生了什么

总结

webApplicatuon 的源码

将以ASPNETCORE_为前缀的环境变量添加到配置中。这意味着,应用可以通过环境变量读取配置,比如ASPNETCORE_ENVIRONMENT用于指定应用运行的环境(开发、生产等)

创建一个主机构建器 HostApplicationBuilder 。

设置 WebRootPath指定静态文件的根目录。

创建一个BootstrapHostBuilder实例,用于初始化和引导应用的启动过程。

使用ConfigureWebHostDefaults方法配置Web主机的默认设置,这包括设置默认的中间件、服务器(如Kestrel)、日志等。 ConfigureApplication方法会被注册到Web主机的配置中,用于设置应用的请求处理管道。

最后,调用InitializeHosting方法完成Web应用托管服务的初始化,这可能包括配置服务容器、应用生命周期管理等

ini 复制代码
public static WebApplicationBuilder CreateBuilder(string[] args) =>
    new(new() { Args = args });

internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilder>? configureDefaults = null)
{
    var configuration = new ConfigurationManager();

    configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_");

    _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
    {
        Args = options.Args,
        ApplicationName = options.ApplicationName,
        EnvironmentName = options.EnvironmentName,
        ContentRootPath = options.ContentRootPath,
        Configuration = configuration,
    });

    // Set WebRootPath if necessary
    if (options.WebRootPath is not null)
    {
        Configuration.AddInMemoryCollection(new[]
        {
            new KeyValuePair<string, string?>(WebHostDefaults.WebRootKey, options.WebRootPath),
        });
    }

    // Run methods to configure web host defaults early to populate services
    var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);

    // This is for testing purposes
    configureDefaults?.Invoke(bootstrapHostBuilder);

    bootstrapHostBuilder.ConfigureWebHostDefaults(webHostBuilder =>
    {
        // Runs inline.
        webHostBuilder.Configure(ConfigureApplication);

        InitializeWebHostSettings(webHostBuilder);
    },
    options =>
    {
        // 不会再次从环境变量中读取配置
        options.SuppressEnvironmentConfiguration = true;
    });

    _genericWebHostServiceDescriptor = InitializeHosting(bootstrapHostBuilder);
}

具体的默认配置

app.UseDeveloperExceptionPage(),开发环境中,自动启用异常页

app.UseAuthentication(); 如果应用配置了鉴权服务,则注册鉴权中间件

app.UseRouting();

app.UseAuthorization(); 如果应用配置了授权服务,则注册授权中间件

连接请求处理管道

app.UseEndpoints(_ => { });

scss 复制代码
private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app, bool allowDeveloperExceptionPage)
{
    Debug.Assert(_builtApplication is not null);

    // UseRouting called before WebApplication such as in a StartupFilter
    // lets remove the property and reset it at the end so we don't mess with the routes in the filter
    if (app.Properties.TryGetValue(EndpointRouteBuilderKey, out var priorRouteBuilder))
    {
        app.Properties.Remove(EndpointRouteBuilderKey);
    }

    if (allowDeveloperExceptionPage && context.HostingEnvironment.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Wrap the entire destination pipeline in UseRouting() and UseEndpoints(), essentially:
    // destination.UseRouting()
    // destination.Run(source)
    // destination.UseEndpoints()

    // Set the route builder so that UseRouting will use the WebApplication as the IEndpointRouteBuilder for route matching
    app.Properties.Add(WebApplication.GlobalEndpointRouteBuilderKey, _builtApplication);

    // Only call UseRouting() if there are endpoints configured and UseRouting() wasn't called on the global route builder already
    if (_builtApplication.DataSources.Count > 0)
    {
        // If this is set, someone called UseRouting() when a global route builder was already set
        if (!_builtApplication.Properties.TryGetValue(EndpointRouteBuilderKey, out var localRouteBuilder))
        {
            app.UseRouting();
            // Middleware the needs to re-route will use this property to call UseRouting()
            _builtApplication.Properties[UseRoutingKey] = app.Properties[UseRoutingKey];
        }
        else
        {
            // UseEndpoints will be looking for the RouteBuilder so make sure it's set
            app.Properties[EndpointRouteBuilderKey] = localRouteBuilder;
        }
    }

    // Process authorization and authentication middlewares independently to avoid
    // registering middlewares for services that do not exist
    var serviceProviderIsService = _builtApplication.Services.GetService<IServiceProviderIsService>();
    if (serviceProviderIsService?.IsService(typeof(IAuthenticationSchemeProvider)) is true)
    {
        // Don't add more than one instance of the middleware
        if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey))
        {
            // The Use invocations will set the property on the outer pipeline,
            // but we want to set it on the inner pipeline as well.
            _builtApplication.Properties[AuthenticationMiddlewareSetKey] = true;
            app.UseAuthentication();
        }
    }

    if (serviceProviderIsService?.IsService(typeof(IAuthorizationHandlerProvider)) is true)
    {
        if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey))
        {
            _builtApplication.Properties[AuthorizationMiddlewareSetKey] = true;
            app.UseAuthorization();
        }
    }

    // Wire the source pipeline to run in the destination pipeline
    var wireSourcePipeline = new WireSourcePipeline(_builtApplication);
    app.Use(wireSourcePipeline.CreateMiddleware);

    if (_builtApplication.DataSources.Count > 0)
    {
        // We don't know if user code called UseEndpoints(), so we will call it just in case, UseEndpoints() will ignore duplicate DataSources
        app.UseEndpoints(_ => { });
    }

    MergeMiddlewareDescriptions(app);

    // Copy the properties to the destination app builder
    foreach (var item in _builtApplication.Properties)
    {
        app.Properties[item.Key] = item.Value;
    }

    // Remove the route builder to clean up the properties, we're done adding routes to the pipeline
    app.Properties.Remove(WebApplication.GlobalEndpointRouteBuilderKey);

    // reset route builder if it existed, this is needed for StartupFilters
    if (priorRouteBuilder is not null)
    {
        app.Properties[EndpointRouteBuilderKey] = priorRouteBuilder;
    }
}

分析

这段代码是WebApplicationBuilder的构造函数,它是ASP.NET Core中用于构建和配置Web应用的基础。构造函数接受一个WebApplicationOptions对象和一个可选的Action<IHostBuilder>委托作为参数。让我们逐步分析这个构造函数的关键部分:

初始化配置管理器

csharp 复制代码
var configuration = new ConfigurationManager();
configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_");
  • 创建一个新的ConfigurationManager实例,用于管理应用的配置。
  • 将以ASPNETCORE_为前缀的环境变量添加到配置中。这意味着,应用可以通过环境变量读取配置,比如ASPNETCORE_ENVIRONMENT用于指定应用运行的环境(开发、生产等)。

创建主机应用构建器

csharp 复制代码
_hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
{
    Args = options.Args,
    ApplicationName = options.ApplicationName,
    EnvironmentName = options.EnvironmentName,
    ContentRootPath = options.ContentRootPath,
    Configuration = configuration,
});
  • 使用提供的WebApplicationOptions和刚创建的配置管理器configuration初始化一个HostApplicationBuilder实例。这个实例用于构建应用的主机,包括设置应用名称、环境名称、内容根路径等。

设置WebRootPath(如果有)

csharp 复制代码
if (options.WebRootPath is not null)
{
    Configuration.AddInMemoryCollection(new[]
    {
        new KeyValuePair<string, string?>(WebHostDefaults.WebRootKey, options.WebRootPath),
    });
}
  • 如果WebApplicationOptions中指定了WebRootPath,则将其添加到配置中。WebRootPath通常用于指定静态文件的根目录。

引导主机构建器

csharp 复制代码
var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);
configureDefaults?.Invoke(bootstrapHostBuilder);
  • 创建一个BootstrapHostBuilder实例,用于初始化和引导应用的启动过程。
  • 如果提供了configureDefaults委托,这里将调用它,允许在引导过程中进一步自定义主机构建器。

配置Web主机默认设置

csharp 复制代码
bootstrapHostBuilder.ConfigureWebHostDefaults(webHostBuilder =>
{
    webHostBuilder.Configure(ConfigureApplication);
    InitializeWebHostSettings(webHostBuilder);
},
options =>
{
    options.SuppressEnvironmentConfiguration = true;
});
  • 使用ConfigureWebHostDefaults方法配置Web主机的默认设置,这包括设置默认的中间件、服务器(如Kestrel)、日志等。
  • 在这个过程中,ConfigureApplication方法会被注册到Web主机的配置中,用于设置应用的请求处理管道。
  • SuppressEnvironmentConfiguration设置为true意味着在这个阶段不会再次从环境变量中读取配置,因为之前已经添加过了。

初始化托管服务

csharp 复制代码
_genericWebHostServiceDescriptor = InitializeHosting(bootstrapHostBuilder);
  • 最后,调用InitializeHosting方法完成Web应用托管服务的初始化,这可能包括配置服务容器、应用生命周期管理等。

总结

这段代码的核心是构建和配置ASP.NET Core应用的主机和Web环境。它通过WebApplicationOptions提供的配置来初始化应用的基础设置(如环境变量、应用名称、根路径等),并利用ASP.NET Core的灵活性来允许进一步的自定义和配置。这是ASP.NET Core应用启动和运行的基础,涵盖了从配置管理到服务注册、中间件配置等关键步骤。

分析

这段代码是ConfigureApplication方法的实现,它是ASP.NET Core框架内部用于配置应用程序请求处理管道的一部分。这个方法主要关注于设置路由、异常处理、鉴权、授权等中间件,并确保应用的中间件和路由配置正确地集成和执行。以下是对该方法关键部分的详细分析:

开发者异常页面

csharp 复制代码
if (allowDeveloperExceptionPage && context.HostingEnvironment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
  • 如果允许使用开发者异常页面且当前环境为开发环境,则注册开发者异常页面中间件。这个中间件在应用发生异常时提供了详细的错误信息,帮助开发者快速定位问题。

路由配置

csharp 复制代码
app.Properties.Add(WebApplication.GlobalEndpointRouteBuilderKey, _builtApplication);
  • 这里通过添加属性,设置WebApplication实例作为全局的路由构建器,这样UseRouting可以使用它来进行路由匹配。
csharp 复制代码
if (_builtApplication.DataSources.Count > 0)
{
    if (!_builtApplication.Properties.TryGetValue(EndpointRouteBuilderKey, out var localRouteBuilder))
    {
        app.UseRouting();
    }
    else
    {
        app.Properties[EndpointRouteBuilderKey] = localRouteBuilder;
    }
}
  • 判断是否已经有路由数据源(即是否定义了路由)。如果有,并且还没有为路由构建器设置本地路由构建器,则调用UseRouting以注册路由中间件。

鉴权与授权中间件

csharp 复制代码
if (serviceProviderIsService?.IsService(typeof(IAuthenticationSchemeProvider)) is true)
{
    if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey))
    {
        _builtApplication.Properties[AuthenticationMiddlewareSetKey] = true;
        app.UseAuthentication();
    }
}
  • 如果应用配置了鉴权服务,则注册鉴权中间件。这里通过检查服务提供器是否包含IAuthenticationSchemeProvider服务来确定。
csharp 复制代码
if (serviceProviderIsService?.IsService(typeof(IAuthorizationHandlerProvider)) is true)
{
    if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey))
    {
        _builtApplication.Properties[AuthorizationMiddlewareSetKey] = true;
        app.UseAuthorization();
    }
}
  • 类似地,如果应用配置了授权服务,则注册授权中间件。通过检查是否存在IAuthorizationHandlerProvider服务来决定。

连接请求处理管道

csharp 复制代码
var wireSourcePipeline = new WireSourcePipeline(_builtApplication);
app.Use(wireSourcePipeline.CreateMiddleware);
  • 这段代码创建了一个中间件,该中间件将确保已构建的应用程序的请求处理管道(即_builtApplication中的配置)被集成到当前应用程序中。

设置和清理属性

csharp 复制代码
foreach (var item in _builtApplication.Properties)
{
    app.Properties[item.Key] = item.Value;
}
  • _builtApplication的属性复制到当前应用构建器的属性中,这包括之前设置的路由构建器等关键信息。
csharp 复制代码
app.Properties.Remove(WebApplication.GlobalEndpointRouteBuilderKey);
if (priorRouteBuilder is not null)
{
    app.Properties[EndpointRouteBuilderKey] = priorRouteBuilder;
}
  • 清理操作:移除全局路由构建器键值对,并在有需要时恢复之前的路由构建器属性。这是为了确保不会干扰到后续的路由配置或启动过滤器中的路由设置。

总结

ConfigureApplication方法通过一系列的步骤确保了应用的中间件和路由配置按照预期工作。它处理了开发者异常页面的注册、路由的配置、鉴权与授权中间件的设置,并且在整个过程中维护了应用的配置状态。这个方法体现了ASP.NET Core中如何灵活地配置和管理Web应用的请求处理管道,同时保持了

高度的可扩展性和可配置性。

相关推荐
计算机学姐41 分钟前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea
JustinNeil1 小时前
简化Java对象转换:高效实现大对象的Entity、VO、DTO互转与代码优化
后端
青灯文案11 小时前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
微尘82 小时前
C语言存储类型 auto,register,static,extern
服务器·c语言·开发语言·c++·后端
计算机学姐2 小时前
基于PHP的电脑线上销售系统
开发语言·vscode·后端·mysql·编辑器·php·phpstorm
码拉松3 小时前
千万不要错过,优惠券设计与思考初探
后端·面试·架构
白总Server4 小时前
MongoDB解说
开发语言·数据库·后端·mongodb·golang·rust·php
计算机学姐4 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
程序员-珍5 小时前
SpringBoot v2.6.13 整合 swagger
java·spring boot·后端
海里真的有鱼5 小时前
好文推荐-架构
后端