Startup类概述
分类: Asp.net Core入门 ◆ 标签: #Asp.Net core基础 #基础 #Web ◆ 发布于: 2023-06-04 20:08:02
写在这个系列的前面,我购买了蒋金楠的书《ASP.net Core 3框架揭秘》这套书分为上下两册,在学习这套书之前我浏览了一遍这套书的内容,这套书的基本架构和微软的官方文档大致相同,主要是作为框架ASP.net Core的基本架构组件的学习,应该说结合微软的官方文档是一个非常好的补充,但是非常可惜的是,当我开始认真的阅读这套书时,我发现这套书的有些内容确实不错,但是阅读到一定的地方我不得不跳过很多内容,主要原因是书的组织上还是有些问题的,另外作者技术水平不用怀疑,但是作者的写作水品是真的有值得提高的地方,虽然这套书的阅读对象肯定不是初入门者,但是书里很多内容立意也是向从易到难,从代码到设计详细的讲清楚,但是很可惜,这方面我认为作者真的要提高,很多内容看了一下就很难看下去。很多知识其实不用这么详细的列代码,只要把内容讲清楚,代码部分只需指出来就可以了,有需要的读者自然会根据提示进一步研究,没有需要的读者也不用迷失在大段的代码中,这完全没必要,毕竟这是一套数,而不是API参考。鉴于这个原因我不得不重新找到微软的官方文档再重看一遍,实际上微软的官方文档是非常好的教材,很多东西都讲得很清楚,有不清楚得地方只需要google一下,然后再结合这套书看一看补充一下。
因此我也决定记录一下这个学习过程,哪怕仅仅是翻译也是可以的。
希望可以帮助到大家。
HongWei 2021/7/26
现在开始今天这个系类的第一篇关于ASP.net Core的Startup类。
Startup类实际上只是一个约定俗称,按照约定大家一般把这个类都起名叫Startup, 那么这个类的作用是什么呢?
- 包含一个服务配置的方法,需要注意的是这个方法其实是可选的,这个方法的名称是:
ConfigureService - 包含一个
Middleware定义的配置方法Configure, 这个方法的主要功能是给用户的请求配置一个处理的pipeline(管道)
所以实际上类Startup的定义也比较简单,大部分情况下只需要包含这个两个方法,而且每个应用的很多配置和初始化工作都是应该在这个类里完成。那么这个类是怎么集成到应用框架里呢?
我们先看一下每个ASP.net Core应用的主要程序入口:Program.cs, 在Program类的定义如下:
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>(); }); }
从这段代码里可以看到:
Startup这个类名真的只是一个惯例,你完全可以用其他的名字,例如:webBuilder.UseStartup<MyBegin>();, 这样这个类就可以定义为名子:MyBegin- 从这段代码里可以看到类
Startup的调用是通过WebHostBuilderExtensions.UseStartup<TStartup>来调用的。
这个是Startup类的本质。
Startup类的构造函数
我们可以在Startup类里定义构造函数,我们前面说过了,Startup类的调用时通过HostBuilder的扩展方法进行调用的,在调用Startup之前,Host已经初始化了一些必要的服务,特别是自动依赖注入的容器和ApplicationService(关于什么是ApplicationService以及它和自动依赖注入之间的区别,我们后面也重新学习), 所以在Startup类里实际上我们已经可以使用自动注入的功能了,但是有限制(这是必然的,因为很多还没有注册到系统里), 那么在Startup类的构造函数里能够自动注入并使用的只能是这样几个:
- IWebHostEnviroment, 表示WebHost当前的环境。
- IHostEnviroment, Host的环境
- IConfiguration, 配置。
例如:
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类
有时候需要根据应用运行的环境,选择多个Startup类,例如开发环境一个Startup类,产线环境一个Startup类, 这个也是很容易实现的,不过一般情况下并不需要使用到这个方案,一般情况下还是通过不同环境的配置来解决不同环境的问题,但是如果要这样做也是可以的,如下是这么做的步骤:
- 定义多个
Startup类,类名的定义形式如下:Startup{EnviromentName}, 例如Development环境,那么类名就是StartupDevelopment, 如果是Produciton,那么类型就是StartupProduction - 在使用
WebHostBuilder.Startup<>时,不用具体的类名,而是使用程序集的名称。
如下是一个实例:
public class StartupDevelopment { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { } } public class StartupProduction { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { } } public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } }
注意上述定义只是为了描述大致的意思,大家可以自行定义相关的内容,我们用如下的代码启用多个Startup类:
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) { var assemblyName = typeof(Startup).GetTypeInfo().Assembly.FullName; return Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(assemblyName); }); } }
注意这里的webBuilder.UseStartup(assemblyName);, 即可以根据环境变量载入多个Startup类,需要注意还是这个方案的应用场景。一般的应用无需使用这个方案。
方法ConfigureService
这个方法在Startup类是可选的,这个方法的主要特征:
- 可选方法
Host在调用Configure方法之前调用这个方法, 这个方法主要用于向Host以及应用注册应用中需要使用到的服务,关于什么服务,我们后面再来讨论,其实质就是向自动注册依赖服务器注册需要的服务。- 按照惯例,配置绑定到IOptions, 也在这个方法里完成。
我们之前也讨论过了在Host调用startup类之前,具体来说在调用ConfigureService方法之前,Host已经初始化了部分的服务,具体来说,默认情况下,Host已经初始化了如下的一些服务, 前面我们讨论过了,在构造函数里我们能够使用的是IEnviorment和IConfiguration, 在方法ConfigureService主要的参数是IServiceCollection, 用于向DI添加必要的服务。
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(); }
关于绑定配置到IOptions, 也可以参考如下代码:
public void ConfigureServices(IServiceCollection services) { services.Configure<PositionOptions>( Configuration.GetSection(PositionOptions.Position)); services.Configure<ColorOptions>( Configuration.GetSection(ColorOptions.Color)); services.AddScoped<IMyDependency, MyDependency>(); services.AddScoped<IMyDependency2, MyDependency2>(); services.AddRazorPages(); }
Configure方法
在Startup类的Configure方法里我们主要是向应用注册需要使用到的中间件(middleware), 所有的middleware串联起来形成一个pipeline, 主要的作用是定义系统如何处理用户的请求,本质上是通过IApplicationBuilder添加middleware到piplein上,习惯上我们使用Use{middleware}的扩展方法来完成向pipeline上添加中间件。
默认的ASP.net Core模板启用了如下的middleware
- 开发异常页面(Developer Exception Page)
- 异常处理(Exception handler)
- HTTP Strict Transport Security (HSTS)
- HTTPS redirection
- 静态文件支持(Static Files)
- MVC或者Razor Pages
关于如何处理Middleware,我们后面的文章也会一一来学习。
不使用Startup类来配置服务和中间件
我们前面也学习了Startup主要是通过扩展方法useStart<>(), 来定义的,那么如果我们不使用Startup如何定义服务和中间件的方法呢?
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"]; }); }); }); }
可以看到实际上也就是在配置host的方法里,通过hostbuild直接调用方法ConfigureService和Configure就可以完成这部分的工作,需要注意的是,要注意调用这个两个方法的顺序。
IStartFilter接口
关于这个接口的详细讨论,您可以参考文章:https://andrewlock.net/exploring-istartupfilter-in-asp-net-core/, 对于这个接口我的总结是:这个接口是另外一条给应用添加中间件的方法,但是它的应用场景是有限的,用法也很简单,只需要在Startup类的ClonfigureService方法里添加必要的服务就可以了,ASP.net Core的runtime会自动调用。一般用户无需关心,如果您确实需要这个功能,请仔细阅读前面给出的文章。
在Startup类中从外部程序集中添加配置
这个部分我们用下一章的内容来学习。