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类中从外部程序集中添加配置
这个部分我们用下一章的内容来学习。