ASP.NET Core 菜鸟之路:从Startup.cs说起 转发https://www.cnblogs.com/chenug/p/6869109.html
1.前言
本文主要是以Visual Studio 2017 默认的 WebApi 模板作为基架,基于Asp .Net Core 1.0,本文面向的是初学者,如果你有 ASP.NET Core 相关实践经验,欢迎在评论区补充。
与早期版本的 ASP.NET 对比,最显著的变化之一就是配置应用程序的方式, Global.asax、FilterConfig.cs 和 RouteConfig.cs 统统消失了,取而代之的是 Program.cs 和 Startup.cs。Program.cs 作为 Web 应用程序的默认入口,不做任何修改的情况下,会调用同目录下 Startup.cs 中的 ConfigureServices 方法 和 Configure 方法。
对于初学者来说,第一次面对 Startup.cs 往往无从下手,本文将一步步介绍作者的经验,但是不会涉入到内部的代码实现以及相关的原理,那并不是本文想要讨论的范畴。
相信我,这将是你迈出构建灵活而强大的ASP.NET Core 应用程序的第一步。
2.配置参数选项
在官方文档中提供多种方式来配置参数选项:
- 文件格式(INI,JSON和XML)
- 命令行参数
- 环境变量
- 内存中的 .NET 对象
- 用户机密存储
- Azure 键值
- 自定义提供程序
虽然提供了很多选择,但是我们只选择其中的JSON文件和环境变量来提供配置参数。
2.1 Json配置参数选项
参考官方文档的示例,我们在 appsettings.json 加入如下的参数:
与此同时,我们还需要一个类来映射这些配置参数:
思考一下 subsection 应该是字典还是一个对象?如果是字典,是否可以为<string,dynamic>或者<string,object>?
好了,现在就差怎么让他们联系起来,只需在 ConfigureServices 方法中将他们配对:
services.Configure<MyOptions>(Configuration);
最后就是解决怎么使用这些配置参数的问题了,举个最简单的例子,我们可以在某个控制器中把我们的所有参数打印出来:
不知道你有没有发现 MyOptions 类中有些属性首字母大写,有些属性没有,并不是与json文件中完全一致,也就是说可以忽略大小写的。
回到之前的匹配环节,我们可以发现 services.Configure 的方法中似乎还有更多选择,比如我们把之前的那一行代码替换为:
services.Configure<MyOptions>(Configuration.GetSection("wizards"));//选择wizards节点
我们可以发现返回的对象里面的属性都为null,这是因为json中的 "wizards"的节点并不能与我们的类匹配。
那么问题来了,如果匹配的代码如下,又会产生什么样的结果呢?先别急着回答,我会在下一节中给出答案。

2.2环境变量
环境变量,或者说系统变量,在windows中我们可以在系统属性中配置:
在Linux环境中也有相应的配置,但是在开发过程中,我们可以在 Visual Studio 中配置:
在这之前,我们的Json文件中已经有 "option1" 和 "option2"的参数选项,那么会产生什么样的结果呢?

显然我们可以看到环境变量的参数覆盖了Json文件的参数。而引起这种变化的原因还是需要回到Startup的初始化:

public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)//必须的json文件,并且自动重载
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)//不必须的json文件
.AddEnvironmentVariables();//启用环境变量
Configuration = builder.Build();
}

从中我们可以看出环境变量的配置在读取 Json 文件参数之后,这样就会覆盖已经存在的同名参数,而已经从 Json 文件被匹配的参数并不会被清空(同样适用于前一节提出的问题)。从另一方面来说,如果你不需要环境变量,则需要去掉 "AddEnvironmentVariables() ",以免覆盖预期参数。
回到本节的中心,我们为什么会需要环境变量呢?我个人会在Dockerfile中配置一些环境变量,比如某种服务的访问地址、某中功能的开关等等。下面举例说说两个常用的环境变量:
ASPNETCORE_URLS 如果你没有指定对应的 Url 监听地址,可以通过该参数修改,如设置为 "http://*:80"。
ASPNETCORE_ENVIRONMENT 开发环境(Development)、预演环境(Staging)、生产环境(Production),将在工作环境一节中做详细介绍。不同的工作环境将使得整个软件流程变得清晰。
2.3配置参数小贴士
- 参数有多种来源,如不需要勿增来源。
- 要注意"最近原则",避免参数同名引起冲突。
- 参数的key可以忽略大小写,所以环境变量中的 "OPTION2" 可以引起覆盖 Json文件中的 "option2" 的效果。
- 为复杂参数选择合适的类型很重要,比如字典还是对象的取舍。
3.依赖注入
依赖注入在 ASP.NET Core 中无处不存在,在之前打印参数的例子中同样用到。依赖注入好处都有啥?为什么我们需要依赖注入?在 ASP .NET Core 中文文档中依赖注入的章节 很好地解释了:
你应该设计你的依赖注入服务来获取它们的合作者。这意味着在你的服务中避免使用有状态的静态方法调用(代码被称为 static cling)和直接实例化依赖的类型。当选择实例化一个类型还是通过依赖注入请求它时,它可以帮助记住这句话, New is Glue。通过遵循 面向对象设计的 SOLID 原则,你的类将倾向于小、易于分解及易于测试。
3.1注册服务以及简单使用
为了方便下一节测试,我准备三个文件,简单的接口、该接口的实现类,拥有接口成员的类:
接下来,我们使用 ASP.NET Core 自带的 DI 来注册服务:
可以看到,注册对象有很多种方法,并且我们可以管理对象的生命周期。注册完对象,我们就可以在我们需要的地方注入对应的对象了,还是最简单的例子——在控制器中使用:
对于控制器,我们有三种方式注入对象:构造函数、控制器动作、属性注入。然而,在一般的类中,使用自带的 DI 只能是构造函数注入。到底是哪种方式好,见仁见智。
3.2生命周期
ASP.NET Core 服务可以被配置为以下生命周期:
- 瞬时(Transient) 在它们每次请求时都会被创建。这一生命周期适合轻量级的,无状态的服务。
- 作用域 (Scoped) 在每次请求中只创建一次。
- 单例(Singleton) 在它们第一次被请求时创建(或者如果你在 ConfigureServices运行时指定一个实例)并且每个后续请求将使用相同的实例。
我们将通过逐步更改 IRepository 的生命周期来看看会发生什么事情。
首先是瞬时:
接着是作用域:
最后是单例:
瞬时很好理解,类似每次都会new了一个对象。而对于作用域,如果一次请求中,有两个甚至多个非单例对象引用到同一个作用域类型时,他们将会收获同一个实例。单例也很好理解,从头到尾都是同一个实例。
控制单一变量,如果只是改变 ProductTotalizer 的生命周期而不是改变 IRepository 的生命周期的话,会发生什么情况呢?
3.3依赖注入小贴士
- 遵循 SOLID 原则,考虑一下哪些是需要依赖注入的。
- 合理考虑生命周期,假如某个类型在不同上下文中需要不同生命周期时,是否需要显式命名区分?还是考虑结构是否合理?
- 避免静态访问服务。
- 避免静态访问 HttpContext 。
4.启用扩展
在项目中我们往往会添加许多扩展,比如用于API文档说明的Swagger、计划任务的Hangfire、压缩响应的GZIP、跨域访问、日志扩展等等。他们的共同点就是需要先安装相应的nuget包,然后在 ConfigureServices() 方法中配置服务,最后在 Configure() 方法中启用。
我们以Swagger为例,首先是安装对应的 nuget 包—— Swashbuckle。
接着是配置扩展:

最后就是启用 Swagger 了:
我们访问 Swagger 的地址看看效果:
5.中间件
中间件是用于组成应用程序管道来处理请求和响应的组件。管道内的每一个组件都可以选择是否将请求交给下一个组件、并在管道中调用下一个组件之前和之后执行某些操作。请求委托被用来建立请求管道,请求委托处理每一个 HTTP 请求。
举一个简单的例子(更复杂的可以在中间件依赖注入对象),从 cookie 中获取 token 并附加到请求头中:

与启用扩展一样,我们同样是需要在 Configure()方法中启用中间件:
app.UseMiddleware<JWTInHeaderMiddleware>();
如果我们有多个中间件呢,中间件的顺序可能会影响到响应结果,但并不是总是线性相关的。例如,我们新增一个对响应状态码处理的中间件:
app.UseMiddleware<StatusCodeMiddleware>();
//....
app.UseMiddleware<JWTInHeaderMiddleware>();
虽然对状态码处理的中间件是最前面,但可以在请求的最后关头对请求结果进行处理。当然,如果中间有某个中间件短路了(没有传递到下一个中间件),就会让我们前功尽弃。
6.过滤器
与中间相似,过滤器同样可以对请求的前后执行特定代码,但是过滤器可以配置为全局有效、仅对控制器有效或是仅对 Action 有效,比中间件更具有灵活性。
另外,过滤器从类型上还能分为:授权过滤器、资源过滤器、Action过滤器、结果过滤器。很容易实现面向切面编程,降低了耦合。
这里举一个我最喜欢的过滤器——对请求的模型进行验证:

当然,模型验证的过滤器往往具有全局性,所以我一般是加在 services.AddMvc 中:
services.AddMvc(config=>
{
config.Filters.Add(new ValidateModel());
});
7.工作环境
ASP.NET Core 提供了许多功能和约定来允许开发者更容易的控制在不同的环境中他们的应用程序的行为。当发布一个应用程序从开发到预演再到生产,为环境设置适当的环境变量允许对应用程序的调试,测试或生产使用进行适当的优化。
在软件开发的生命周期中,在不同的工作环境中往往是不同的状态。比如说,在测试或者预演环境中,启用Swagger扩展、控制台打印日志、允许跨域,而在生产环境中,往往处于安全、性能或者其他考虑,这些功能是需要禁止的。对于 MVC 开发者来说,在开发过程中会使用本地的JS、Css、图片等文件,而在生产环境中这些文件往往是从CDN中获取。
8.结语
ASP.NET Core 提供了强大而灵活的配置机制,每个点展开都像是一片新的天地。即使是经验丰富的开发者,也不能自称完全掌握全部机制。随着 .NET Core 的完善和发展,Startup.cs 也将越来越复杂。越是复杂,就越是要小心,如无需要,勿增实体!
9.相关引用
ASP.NET Core 菜鸟之路:从Startup.cs说起 转发https://www.cnblogs.com/chenug/p/6869109.html的更多相关文章
- 探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs
原文:探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs 前言:.NET Core 3.0 SDK包含比以前版本更多的现成模板. 在本文中,我将 ...
- ASP.NET Core 菜鸟之路:从Startup.cs说起
1.前言 本文主要是以Visual Studio 2017 默认的 WebApi 模板作为基架,基于Asp .Net Core 1.0,本文面向的是初学者,如果你有 ASP.NET Core 相关实践 ...
- Asp.Net Core 入门(二)——Startup.cs做了什么
上篇介绍了Program.cs中Main做了什么,这篇我们来讨论下Startup.cs它又做了什么呢? 我们新建一个Asp.Net Core Mvc项目,先来开一下Startup的代码 public ...
- asp.net core 系列 2 启动类 Startup.CS
学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 在探讨Startup启动类之前,我们先来了解下Asp.NET CORE 配置应用程序的执行顺序 ...
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...
- ASP.NET Core 1.0 入门——Application Startup
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- ASP.NET Core[源码分析篇] - Startup
应用启动的重要类 - Startup 在ASP.NET Core - 从Program和Startup开始这篇文章里面,我们知道了Startup这个类的重要性,它主要负责了: 配置应用需要的服务(服务 ...
- ASP.NET Core 2 preview 1中Program.cs,Startup.cs和CreateDefaultBuilder的探索
Exploring Program.cs, Startup.cs and CreateDefaultBuilder in ASP.NET Core 2 preview 1 ASP.NET Core 2 ...
- Asp.Net Core 第03局:Startup
总目录 前言 本文介绍Startup,它主要用于配置应用使用的服务和应用的请求管道. 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:Startup ...
随机推荐
- word中迅速将表格一分为二 拆分表格快捷键ctrl+shift+enter 重复上一个命令快捷键f4
这里说的是将一个表格拆分为两个表格 选择要拆分的行,快捷键ctrl+shift+enter,就拆分为两个表格了,是不是很快! 在多个表格需要拆分的时候,做一次这样的操作,然后不停的移动.F4,就可以了 ...
- [Python3] 021 面向对象 第一弹
目录 1. 面向对象概述 1.1 OOP 思想 1.2 几个名词 1.3 类与对象 2. 类的基本实现 2.1 类的命名 2.2 如何声明一个类 2.3 如何实例化一个类 2.4 如何访问对象成员 2 ...
- 合并石子 (区间dp+前缀和)
[题目描述] N堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.计算出将N堆石子合并成一堆的最小得分. [题目链接] http: ...
- 2019牛客暑期多校训练营(第一场) - H - XOR - 线性基
https://ac.nowcoder.com/acm/contest/881/H 题意: 给定n个整数,求其中异或和为 \(0\) 的子集的大小的和. 题解思路: 首先转化为每个可以通过异或表示 \ ...
- C. Trailing Loves (or L'oeufs?) (质因数分解)
C. Trailing Loves (or L'oeufs?) 题目传送门 题意: 求n!在b进制下末尾有多少个0? 思路: 类比与5!在10进制下末尾0的个数是看2和5的个数,那么 原题就是看b进行 ...
- 1571. [Usaco2009 Open]滑雪课Ski
传送门 可以想到 $dp$,设 $f[i][j]$ 表示当前等级为 $i$,时间为 $j$ 的最大滑雪次数 显然上课不会上让自己等级降低的课,所以第一维 $i$ 满足无后效性 然后直接枚举 $i,j$ ...
- NGUI的下拉框的使用(popup list script)
一,我们先添加一个sprite,选择sprite,右键选择attach,添加box collider, 然后右键选择attach,添加popup list script ,我们给popup list ...
- Django中的APP
3. Django中的APP: 什么是APP?以及为什么要用APP? project --> 项目 (老男孩教育大学校) APP --> 应用 (Linux学院/Python学院/大数据学 ...
- Axis2 客户端调用 设置超时时间
我用的是axis2-1.6.2版本.请看下面的客户端代码: import org.apache.axis2.client.Options; import com.ctis.ta.service.imp ...
- 计蒜客NOIP模拟D1T2
原题: 蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少.另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离 ...