原文链接(英)
本文内容适用于ASP.NET Core 5.0
“启动” 类启动 类用于配置应用程序依赖的 服务(services) 和用于处理客户端http请求的 流水线(pipeline)
ASP.NET Core 应用程序使用 启动 类(这个 类 按照约定命名为 Startup
)完成如下启动任务:
[可选] Startup
类包括一个 ConfigureServices
方法用于配置应用程序依赖的 服务(services) , 服务 是为应用程序提供某项功能的可重用组件。 服务 通过 ConfigureServices
方法进行注册,注册后的服务全局可用,使用的方式是通过 依赖注入(DI) 或 ApplicationServices
。
Startup 类包括一个 Configure 方法,此方法用于创建处理客户端http请求的 流水线(pipeline)
Startup
类的 ConfigureServices
方法和 Configure
方法在应用程序启动时被ASP.NET Core 运行时调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error" ); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } }
上面的代码针对的是 Razor Pages 编程框架,使用 MVC 框架的代码和这段代码类似。
Startup
类是当应用程序的host
被构建时指定的。通常情况下,Startup
类通过 host
构造器(builder)的 WebHostBuilderExtensions.UseStartup<TStartup>
方法来指定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Program { public static void Main(string [] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string [] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
主机(host) 通过 Startup
类的 构造器(constructor) 为 Startup
类提供其所依赖的服务。应用程序通过 Startup
类的 ConfigureServices
方法注册应用程序依赖的其它服务。 主机 提供的 服务 和应用程序提供的 服务 在 Startup
类的 Confugure
方法和应用程序的全局都可用。
当使用 通用主机(Generic Host [IHostBuilder]) 时,只有下面的服务可以被注入到 Startup
类的 构造器 :
IWebHostEnvironment
IHostEnvironment
IConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Startup { private readonly IWebHostEnvironment _env; public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; _env = env; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { if (_env.IsDevelopment()) { } else { } } }
多数 服务 在 Startup
类的 Confugure
方法被调用前是不可用的。
多重启动 当应用程序为不同的环境定义不同的 启动类 (例如: StartupDevelopment
),对应该环境的 启动类 会在运行时被选择。类名的后缀匹配当前运行环境的 启动类 被优先应用。比如,如果应用程序运行在开发环境下,并且定义了 StartupDevelopment
和 Startup
两个类,那么 StartupDevelopment
将会被运行时应用。
ConfigureServices
方法是可选的。
它由 主机 在 Configure
方法被调用之前调用,用来配置应用程序所依赖的 服务 ,主机 可能在 Startup
类的构造函数被调用前配置一些 Startup
类要用到的服务。
对于那些经常用到的 服务 ,IServiceCollection
定义了一些形似 Add{Service}
的扩展方法,例如:AddDbContext
、 AddDefaultIdentity
、 AddEntityFrameworkStores
、 AddRazorPages
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Startup{ public Startup(IConfiguration configuration ) { Configuration = configuration ; } public IConfiguration Configuration { get ; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options .UseSqlServer( Configuration .GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<IdentityUser>( options => options .SignIn.RequireConfirmedAccount = true ) .AddEntityFrameworkStores<ApplicationDbContext>(); services.AddRazorPages(); }
将 服务 添加到 服务容器 使得这些服务在 Startup
类的 Configure
方法中可用,并且在应用程序的全局可用,这些服务可以通过 依赖注入 机制或从 ApplicationServices
中取得。
Configure
方法用于配置应用程序如何响应HTTP请求,它通过将 中间件(middleware) 组件添加到 IApplicationBuilder
实例来配置处理HTTP请求的 流水线(pipeline) ,IApplicationBuilder
对Configure
方法可用,但是它并没有被注册到 服务容器 , 宿主 创建IApplicationBuilder
实例并将它做为参数直接传递给Configure
方法。
ASP.NET Core应用程序模板配置了支持如下功能的HTTP处理 流水线 :
开发者异常页面(Developer Exception Page)
异常处理(Exception handler)
HTTP Strict Transport Security (HSTS)
HTTPS重定向(HTTPS redirection)
静态文件(Static files)
ASP.NET Core MVC and Razor Pages
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error" ); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } }
上面的代码针对的是 Razor Pages 编程框架,使用 MVC 框架的代码和这段代码类似。
每一个 Use{XXX}
扩展方法添加一个 中间件 到HTTP请求处理 管道线 ,例如:UseStaticFiles
配置了用于处理静态文件的中间件。
流水线 中的每个中间件处理完自己的任务后负责调用流水线中的下一个中间件,或者如果满足某个特定条件的话 短路 流水线的处理流程,结束HTTP处理。
其它的服务 ,比如IWebHostEnvironment
、ILoggerFactory
或者其它任何定义在ConfigureServices
方法中的中间件,都能通过Configure
方法的参数签名来指定。然后这些参数中指定服务如果可用的话就会被依赖注入 机制注入。
不通过Startup
类来配置服务 如果不想通过Startup
类来配置应用程序依赖的服务 及HTTP处理流水线,那么可以通过调用宿主 建造器(builder)的ConfigureServices
方法和Configure
方法来实现同样的功能。对ConfigureServices
的多次调用会依次将服务 加入到服务容器 ,如果对Configure
多次调用,那么最后一次的调用生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Program { public static void Main(string [] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string [] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config ) => { }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureServices(services => { services.AddControllersWithViews(); }) .Configure(app => { var loggerFactory = app.ApplicationServices .GetRequiredService<ILoggerFactory>(); var logger = loggerFactory.CreateLogger<Program>(); var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>(); var config = app.ApplicationServices.GetRequiredService<IConfiguration>(); logger.LogInformation("Logged in Configure" ); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error" ); app.UseHsts(); } var configValue = config["MyConfigKey" ]; }); }); }); }
通过 启动筛选器(startup filters) 扩展Startup
类 使用IStartupFilter
: 这个主题不太容易理解,请参见这篇对IStartupFilter
进行详细研究的文章 。
可在应用程序通过Startup.Configure
配置的中间件流水线之前或之后配置其它的中间件,而不用显式调用Use{Middleware}
。ASP.NET Core使用IStartupFilter
在http处理流水线的开头添加默认处理中间件,而不必要求应用程序的作者显式地注册默认的中间件。通过IStartupFilter
,不同的组件可以以应用程序作者的名义调用Use{Middleware}
。
创建一个Configure
方法调用的流水线。IStartupFilter.Configure
可以设置一个中间件在某个库添加的http处理中间件 这前或之后运行。
IStartupFilter
实现了一个Configure
方法,此方法接收并返回一个Action<IApplicationBuilder>
。IApplicationBuilder
定义了一个用于配置应用程序http请求处理流水线 的类。
每个IStartupFilter
可以添加一个或多个中间件到http请求处理流水线,筛选器按照它们被添加到服务容器 中的顺序被调用。
下面这个例子演示了如何通过IStartupFilter
注册一个中间件,例子中的RequestSetOptionsMiddleware
中间件从http请求的查询字符串 参数中取得需要的值,并将这个值赋给httpContext.Items["option"]
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RequestSetOptionsMiddleware { private readonly RequestDelegate _next; public RequestSetOptionsMiddleware( RequestDelegate next ) { _next = next; } public async Task Invoke(HttpContext httpContext) { var option = httpContext.Request.Query["option" ]; if (!string .IsNullOrWhiteSpace(option)) { httpContext.Items["option" ] = WebUtility.HtmlEncode(option); } await _next(httpContext); } }
RequestSetOptionsMiddleware
在RequestSetOptionsStartupFilter
类中进行配置:
1 2 3 4 5 6 7 8 9 10 11 public class RequestSetOptionsStartupFilter : IStartupFilter { public Action <IApplicationBuilder > Configure (Action <IApplicationBuilder > next) { return builder => { builder.UseMiddleware <RequestSetOptionsMiddleware >(); next(builder); }; } }
IStartupFilter
通过IHostBuilder.ConfigureServices
注册在服务容器 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Program { public static void Main(string [] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string [] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config ) => { }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .ConfigureServices(services => { services.AddTransient<IStartupFilter, RequestSetOptionsStartupFilter>(); }); }
当http请求的查询字符串 中包含option
参数的值时,RequestSetOptionsMiddleware
会在ASP.NET Core中间件渲染 响应(response) 之前处理httpContext.Items["option"]
的赋值操作。
中间件按照IStartupFilter
注册的顺序执行: