总结
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应用的请求处理管道,同时保持了
高度的可扩展性和可配置性。