深入理解ASP.NET Core中的Program类和Startup类

news2024/9/23 15:23:38

一、背景介绍

本文以ASP.NET Core 6以前版本API程序来说明。

  在我们新建ASP.NET Core项目时,项目根目录下会自动建立Program.cs和Startup.cs两个类文件。

  Program.cs 作为 Web 应用程序的默认入口,不做任何修改的情况下,会调用同目录下 Startup.cs 中的 ConfigureServices 方法 和 Configure 方法。

  应用启动的流程如下图所示:

  本文主要介绍Startup.cs 类,认识其功能和作用,深入了解其内部运行机制,以便于更好地在项目中进行应用。

二、program类介绍

        program.Main方法是应用程序的托管入口。

        在构建应用程序的主机(WebHost)时,在主程序的Web Host生成器(IWebHostBuilder)的 UseStartup <TStartup> 扩展方法中指定启动类名称。ASP.NET Core应用程序的启动类,按照惯例命名为Startup。而Main入口通过主机生成器(IWebHostBuilder)调用Build时,生成对应的应用程序的主机(WebHost),并启动运行(Run)

Host 是一个静态类,包含了两个方法: CreateDefaultBuilder() 、CreateDefaultBuilder (args)

在下面源码中我将会剖析这两个方法的用途:

public static class Host
 {
       public static IhostBuilder CreateDefaultBuilder()
        public static IHostBuilder CreateDefaultBuilder(string[] args);
    }

源码:
public static class Host
    {
       public static IHostBuilder CreateDefaultBuilder() =>
            CreateDefaultBuilder(args: null);
 
        public static IHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new HostBuilder();
 
            builder.UseContentRoot(Directory.GetCurrentDirectory());
            builder.ConfigureHostConfiguration(config =>
            {
                config.AddEnvironmentVariables(prefix: "DOTNET_");
                if (args != null)
                {
                    config.AddCommandLine(args);
                }
            });
 
            builder.ConfigureAppConfiguration((hostingContext, config) =>
            {
                IHostEnvironment env = hostingContext.HostingEnvironment;
 
                bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
 
                config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
                      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
 
                if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
                {
                    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                    if (appAssembly != null)
                    {
                        config.AddUserSecrets(appAssembly, optional: true);
                    }
                }
 
                config.AddEnvironmentVariables();
 
                if (args != null)
                {
                    config.AddCommandLine(args);
                }
            })
            .ConfigureLogging((hostingContext, logging) =>
            {
                bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
 
                if (isWindows)
                {
                   
                    logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
                }
 
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
                logging.AddEventSourceLogger();
 
                if (isWindows)
                {
                    logging.AddEventLog();
                }
 
                logging.Configure(options =>
                {
                    options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
                                                        | ActivityTrackingOptions.TraceId
                                                        | ActivityTrackingOptions.ParentId;
                });
 
            })
            .UseDefaultServiceProvider((context, options) =>
            {
                bool isDevelopment = context.HostingEnvironment.IsDevelopment();
                options.ValidateScopes = isDevelopment;
                options.ValidateOnBuild = isDevelopment;
            });
 
            return builder;
        }

从源码可以看出:

        1.首先无参的CreateDefaultBuilder()方法只是通过调用传递null值来调用有参的CreateDefaultBuilder(string[] args)方法

        2.var builder = new HostBuilder();

CreateDefaultBuilder(string[] args) 方法从创建HostBuilder 类型对象的实例化开始(继承自IHostBuilder),我们可以直接在这个实例上调用Build.Run()方法来启动我们的ASP.NET 主机,但是只是这样调用的话,并没有对我们的主机以及应用程序做配置,那么怎么配置呢?
        3.Host Configuration(主机配置根目录)

builder.UseContentRoot(Directory.GetCurrentDirectory());

        4.应用程序配置(Setting up the app)
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
    IHostEnvironment env = hostingContext.HostingEnvironment;
 
    bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
 
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
 
    if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
    {
        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
        if (appAssembly != null)
        {
            config.AddUserSecrets(appAssembly, optional: true);
        }
    }
 
    config.AddEnvironmentVariables();
 
    if (args != null)
    {
        config.AddCommandLine(args);
    }

        5.hostingContext.HostingEnvironment表示我们在开发或生产环境信息。
bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);
 
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);

        在程序启动配置时,它会加载我们项目中的appsettings.json及其下面的配置json 文件。通过AddJsonFile()这个方法来实现,这个方法有三个入参:

Path(string): .json 文件的相对路径位置
Optional(bool): 指定文件是否是必须的,如果设置为false,那么如果找不到文件则会抛出文件找不到异常。
ReloadOnchange(bool): 如果设置为true,那么我们改变配置文件,那么应用程序也会随之更改而无需重新启动。
bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true);

        这段代码是.NET5 新增的,在之前.net core的应用程序中,reloadOnchange的值始终是ture,至此我们就可以对hostBuilder:reloadConfigOnChange 配置为false;
if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) 

    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); 
    if (appAssembly != null) 
    { 
        config.AddUserSecrets(appAssembly, optional: true); 
    } 

这段程序的作用是如果我们需要加密我们的appsettings.json文件,则可以使用:具体使用没看。

        6.配置日志(Logging)

.ConfigureLogging((hostingContext, logging) =>
 {
     bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
 
     // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
     // the defaults be overridden by the configuration.
     if (isWindows)
     {
         // Default the EventLogLoggerProvider to warning or above
         logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
     }
 
     logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
     logging.AddConsole();
     logging.AddDebug();
     logging.AddEventSourceLogger();
 
     if (isWindows)
     {
         // Add the EventLogLoggerProvider on windows machines
         logging.AddEventLog();
     }
 
     logging.Configure(options =>
     {
         options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
                                             | ActivityTrackingOptions.TraceId
                                             | ActivityTrackingOptions.ParentId;
     });
 })

上述代码用来设置应用程序的生成日志;从代码中可以看出如果程序跑在windows环境下,级别是或者高于警告级别的日志将会被输出在控制台,VS输出窗口以及写入系统日志服务。具体如何操作暂时没看懂。

        7.依赖注入

.UseDefaultServiceProvider((context, options) =>
{
    bool isDevelopment = context.HostingEnvironment.IsDevelopment();
    options.ValidateScopes = isDevelopment;
    options.ValidateOnBuild = isDevelopment;
});

CreateDefaultBuilder 方法的最后一步是将依赖注入添加到我们的应用程序中。UseDefaultServiceProvider这个方法用来添加.NET 5 或者.NET Core自带的IOC容器。

        8.对 .ConfigureWebHostDefaults(webBuilder =>
                        {        
                    webBuilder.UseStartup<Startup>();
                       });分析

       public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure);是一个静态方法返回Ihostbulider,通过CreateDefaultBuilder方法从创建然后进行进一步通过startup类进行配置组件和容器的组件。

 CreateHostBuilder(args).Build().Run()

主机build源码

主机Build的过程主要完成了:

  • BuildHostConfiguration:构造配置系统,初始化 IConfiguration _hostConfiguration;
  • CreateHostingEnvironment:构建主机HostingEnvironment环境信息,包含 ApplicationName、 EnvironmentName、 ContentRootPath等
  • CreateHostBuilderContext:创建主机Build上下文HostBuilderContext,上下文中包含: HostingEnvironment和 Configuration
  • BuildAppConfiguration:构建应用程序配置
  • CreateServiceProvider:创建依赖注入服务提供程序, 即依赖注入容器

 三、Startup类介绍              

          Startup类用于配置服务和应用程序的请求管道。

        因为主机启动时,执行顺序为Startup构造函数 -> ConfigureServices方法 -> Configure 方法。在Startup构造函数执行时主机只提供了这三个服务,别的服务需要在ConfigureServices方法中添加。然后到了Configure方法执行的时候,就可以使用更多的服务类型了

 其中,Startup类必须定义Configure方法,但是可选择定义一个ConfigureServices 方法,这些方法将在应用程序启动时被调用。

 1、ConfigureServices方法:用于设置应用程序所需要的服务。

  ConfigureServices 方法在Configure方法配置应用程序服务之前被主机(WebHost)调用。其中按常规设置配置选项(appsettings.json)。

  对于需要大量设置的功能,IServiceCollection 上有 Add{Service} 扩展方法,将服务添加到服务容器,使其在应用程序和Configure方法中可用。服务通过依赖关系注入(DI)或 ApplicationServices 进行解析。例如:

 

2、Configure方法:用于指定应用程序响应HTTP请求的方式。

  可通过将中间件(middleware)组件添加到IApplicationBuilder实例来配置请求管道。Configure方法可使用 IApplicationBuilder,但未在服务容器中注册。托管创建 IApplicationBuilder并将其直接传递到Configure。

  请求管道中的每个中间件组件负责调用管道中的下一个组件,或在适当情况下使链发生短路。 如果中间件链中未发生短路,则每个中间件都有第二次机会在将请求发送到客户端前处理该请求。该方法接受IApplicationBuilder作为参数,同时还可以接收其他一些可选参数,如IHostingEnvironment和ILoggerFactory。一般而言,只要将服务注册到configureServices方法中时,都可以在该方法中使用。

 

每个Use扩展方法将一个中间件组件添加到请求管道。 例如,UseMvc扩展方法将路由中间件添加到请求管道,并将MVC配置为默认处理程序。

3、扩展Startup方法:IStartupFilter

  使用IStartupFilter来对Startup功能进行扩展,在应用的Configure中间件管道的开头或末尾使用IStartupFilter来配置中间件。IStartupFilter有助于确保当库在应用请求处理管道的开端或末尾添加中间件的前后运行中间件。

  IStartupFilter接口存在于Microsoft.AspNetCore.Hosting.Abstractions程序集中,它仅定义了一个接口方法。

其中Configure方法返回了一个变量Action

  当创建一个ASP.NET Core应用程序的时候,IApplicationBuilder负责配置ASP.NET Core的中间件管道。例如:

在这个方法中,可以直接使用IApplicationBuilder参数,并且可以向其中添加各种中间件。

  使用IStartupFilter, 可以指定并返回一个Action类型的泛型委托,这意味除了可以使用方法提供的泛型委托配置IApplicationBuilder对象, 还需要返回一个泛型委托。

  IStartupFilter方法可以接受一个配置IApplicationBuilder的方法,换而言之IStartupFilter.Configure方法可以使用Startup.Configure方法作为参数。例如:

 

关于IStartupFilter的例子 

 

注册IStartupFilter 

Startup 类的 ConfigureServices 方法中,将自定义的启动过滤器添加到依赖注入容器。

 自定义的启动过滤器会在应用程序启动时自动被调用。你不需要手动调用它,它会在应用程序启动之前和之后执行你在 Configure 方法中定义的逻辑。你可以在适当的地方添加你的自定义逻辑,例如初始化设置、日志记录等。

在应用程序的启动过程中,预配置和初始化任务可以用于确保应用程序处于正确的状态并能够正常运行。以下是一些常见的预配置和初始化任务场景:

  1. 配置加载: 在应用程序启动时,加载应用程序的配置文件,将配置信息加载到内存中,以便后续使用。这可以包括数据库连接字符串、API 密钥、应用程序设置等。

  2. 数据库迁移: 如果你使用了数据库,可以在应用程序启动时执行数据库迁移,确保数据库的结构与代码的期望一致。这有助于在应用程序更新时保持数据库的同步。

  3. 依赖注入配置: 在应用程序启动时,配置依赖注入容器,将各个服务注册到容器中,以便在整个应用程序中使用。这可以确保在运行时能够正确解析和注入依赖项。

  4. 缓存预热: 预先加载某些数据到缓存中,以减少后续请求的响应时间。这可以提高应用程序的性能,尤其是对于需要频繁查询的数据。

  5. 日志记录配置: 配置应用程序的日志记录器,以便在应用程序启动时就能够记录日志,以及配置日志记录级别和目标(文件、数据库、远程日志服务等)。

  6. 资源初始化: 初始化应用程序需要的资源,比如在应用程序启动时加载某些静态文件、图像、字体等。

  7. 环境检查: 检查应用程序运行所需的环境是否满足要求,例如检查数据库是否可用、网络是否连接等。如果环境不满足要求,可以在启动时提前发现并报错。

  8. 权限验证和安全性配置: 进行身份验证和授权配置,确保只有有权限的用户能够访问应用程序的特定部分。

  9. 任务调度器和定时作业: 配置定时任务,以便在应用程序启动时启动定时作业,执行后台任务,如数据同步、报告生成等。

总之,预配置和初始化任务可以帮助确保应用程序在启动时处于正确的状态,以便能够正常运行和提供所需的功能。这些任务可以在应用程序启动之前或之后执行,具体取决于需求。

四、net core启动过程

一、启动执行顺序
1.ConfigureWebHostDefaults
一般是配置组件和容器的组件

2.ConfigureHostConfiguration
用来配置HostBuilder的builder过程所用到的配置的。

3.ConfigureAppConfiguration
嵌入自己的配置文件供应用程序来读取。

4.ConfigureServices
往容器里面注入应用组件

5.ConfigureLogging
注入日志组件

6.Startup
执行Startup类的构造函数

7.ConfigureServices
执行Startup类里面ConfigureServices来注入一些服务组件

8.Configure
执行Startup类里面Configure来注入中间件,处理HttpContext整个请求过程。

区别:ConfigureServices 负责将组件注册到容器中;而Configure 是配置Http处理管道的过程;
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/901191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dubbo 融合 Nacos 成为注册中心

快速上手 Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单&#xff0c;大致步骤可分为“增加 Maven 依赖”以及“配置注册中心“。 增加 Maven 依赖 只需要依赖Dubbo客户端即可&#xff0c;关于推荐的使用版本&#xff0c;请参考Dubbo官方文档或者咨询Dubbo开发人员&#…

Vue 2 组件基础

一个简单的组件示例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</titl…

暴力模拟入门+简单:零件组装、塔子的签到题、塔子哥考试、平均像素值、换座位

暴力模拟入门 P1038 小红书-2022.9.23-零件组装 #include <bits/stdc.h> #include <cstdint> using namespace std;typedef long long LL; const int N 100001; int num[4]; LL d; vector<vector<LL>> v(4, vector<LL>(N));int main() {for(in…

python中的__name__是个啥?

pycharm中随便打开一个文件&#xff0c;在special variables中能看到一个__name__的变量 在很多python脚本中&#xff0c;也经常能看到if name "main"这样一行 所以_name_到底是个啥&#xff1f; 首先&#xff0c;我们可以确定这是一个str字符变量 “在 Python 中&…

06_布隆过滤器BloomFilter

06——布隆过滤器BloomFilter 一、是什么 由一个初始值都为零的bit数组和多个哈希函数构成&#xff0c;用来快速判断集合中是否存在某个元素 设计思想&#xff1a; 1. 目的&#xff1a;减少内存占用 1. 方式&#xff1a;不保存数据信息&#xff0c;只是在内存中做一个是否存…

【框架类】—MVVM框架

一、MVVM框架有哪些 Vue.jsReact.jsAngular.js 二、对MVVM的认识 1. MVC是什么 全称 Model View Controller, 它采用模型(Model)-视图(View)-控制器(controller)的方法把业务逻辑、数据与界面显示分离 2. MVVM的定义 MVVM是一种软件架构模式&#xff0c;它代表了模型 --视…

智慧工地监管一体化云平台源码 PC端、 手机端、 现场端

智慧工地管理平台是以物联网、移动互联网技术为基础&#xff0c;充分应用大数据、人工智能、移动通讯、云计算等信息技术&#xff0c;利用前端信息采通过人机交互、感知、决策、执行和反馈等&#xff0c;实现对工程项目內人员、车辆、安全、设备、材料等的智能化管理&#xff0…

Python 潮流周刊#16:优雅重要么?如何写出 Pythonic 的代码?

你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。标题取自其中两则分享&#xff0c;不代表全部内容都是该主题&#xff0c;特此声明。 本周刊由 Python猫 出品&#xff0c;精心筛选国内外的 250 信息源&#xff0c;为你挑选…

Linux(入门篇)

Linux&#xff08;入门篇&#xff09; Linux概述Linux是什么Linux的诞生Linux和Unix的渊源GNU/LinuxLinux的发行版Linux VS Windows Linux概述 Linux是什么 Linux是一个操作系统(OS) Linux的诞生 作者&#xff1a;李纳斯托瓦兹&#xff08;git也是他开发的&#x1f602;&am…

11. 实现业务功能--获取用户信息

目录 1. 实现 Controller 2. 单体测试 3. 修复返回值存在的缺陷 3.1 用户的隐私数据&#xff1a;密码的密文和盐不能显示 3.2 将值为 null 的字段可以进行过滤 3.3 时间的格式需要进行处理&#xff0c;如 yyyy-mmmm-ddd HH:mm:ss 3.4 data 属性没有返回 4. 实现前端页…

低代码平台全套源码,支持二次开发

低代码开发平台&#xff1a;只需要编写简单的配置文件即可构建企业级应用程序。 一、低代码PaaS平台可以在云端开发、部署、运行低代码应用程序。使用独立数据库模型&#xff0c;基于Kubernetes云原生技术&#xff0c;每个租户均可拥有一套独立的存储、数据库、代码和命名空间&…

光栅化之扫描填充三角形

重心坐标计算 重心坐标比较简单&#xff0c;取最大包围合再计算点是否在三角形内就行&#xff0c;再根据重心坐标返回的alpha,beta,gamma三个权重值计算 uv映射和depth深度缓冲值&#xff0c;因为是求的重心坐标&#xff0c;感觉效果比插值的要好一点。 求重心坐标 barycentr…

Qt 编译使用Bit7z库接口调用7z.dll、7-Zip.dll解压压缩常用Zip、ISO9660、Wim、Esd、7z等格式文件(一)

bit7z一个c静态库&#xff0c;为7-zip共享库提供了一个干净简单的接口 使用CMAKE重新编译github上的bit7z库&#xff0c;用来解压/预览iso9660&#xff0c;WIm&#xff0c;Zip,Rar等常用的压缩文件格式。z-zip库支持大多数压缩文件格式 导读 编译bit7z(C版本)使用mscv 2017编译…

C# 把dll打包到exe文件,真的可以 。文件批量转了ANSI编码

在 C# 中&#xff0c;将 DLL 文件打包到 EXE 文件中可以使用 ILRepack 工具。ILRepack 是一个开源的工具&#xff0c;可以合并多个 DLL 文件并将它们嵌入到一个 EXE 文件中&#xff0c;从而实现将 DLL 打包到 EXE 的功能。 以下是使用 ILRepack 工具打包 DLL 到 EXE 的步骤&…

CSDN今日热榜词云图

文章目录 C云原生人工智能和Python前沿技术软件工程后端javajavascriptphp区块链大数据移动开发嵌入式开发工具数据结构与算法微软技术测试游戏网络运维 C C果然还是应试语言&#xff0c;真题的占比竟然这么大。C之所以没出现&#xff0c;很有可能是在做词云的时候把加号当作非…

多人联机对战游戏赛道,你准备好了吗?

用户日益增长的精神需求和社交娱乐需要&#xff0c;让联机对战的需求与日剧增。 硬件和网络技术的高速发展&#xff0c;也使得联机游戏的体验越来越好。 可以看到&#xff0c;越来越多的联机对战游戏登上游戏榜单。 联机对战已逐渐成为主流&#xff0c;无论在哪个游戏榜单&…

二,MySQL数据库主从复制的介绍及搭建(收藏)

一&#xff0c;介绍概述 主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持同步。 DDL&#xff1a;数据定义语言&#xff0c;用…

HRS--人力资源系统(Springboot+vue)--打基础升级--(二)写个主菜单导航界面

1. 华为OD机考题 答案 2023华为OD统一考试&#xff08;AB卷&#xff09;题库清单-带答案&#xff08;持续更新&#xff09; 2023年华为OD真题机考题库大全-带答案&#xff08;持续更新&#xff09; 2. 面试题 一手真实java面试题&#xff1a;2023年各大公司java面试真题汇总--…

计算机 数进制转换;存储MB与带宽Mbps

参考&#xff1a;https://zhuanlan.zhihu.com/p/459817484 1、计算机 数进制转换 1&#xff09;与十进制相关的转换 2&#xff09;与二进制相关的转换 二进制是Binary&#xff0c;简写为B&#xff1b;八进制是Octal&#xff0c;简写为O&#xff1b;十进制是Decimal&#xff…