ASP.NET Core 菜鸟之路:从Startup.cs提及

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 方法。html


应用启动的流程


对于初学者来讲,第一次面对 Startup.cs 每每无从下手,本文将一步步介绍做者的经验,可是不会涉入到内部的代码实现以及相关的原理,那并非本文想要讨论的范畴。编程


默认的Startup.cs


相信我,这将是你迈出构建灵活而强大的ASP.NET Core 应用程序的第一步。json

2.配置参数选项

在官方文档中提供多种方式来配置参数选项:windows

  • 文件格式(INI,JSON和XML)
  • 命令行参数
  • 环境变量
  • 内存中的 .NET 对象
  • 用户机密存储
  • Azure 键值
  • 自定义提供程序

虽然提供了不少选择,可是咱们只选择其中的JSON文件和环境变量来提供配置参数。跨域

2.1 Json配置参数选项

参考官方文档的示例,咱们在 appsettings.json 加入以下的参数:安全


appsettings.json


与此同时,咱们还须要一个类来映射这些配置参数:cookie


MyOptions.cs

思考一下 subsection 应该是字典仍是一个对象?若是是字典,是否能够为<string,dynamic>或者<string,object>?app

好了,如今就差怎么让他们联系起来,只需在 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注册服务以及简单使用

为了方便下一节测试,我准备三个文件,简单的接口、该接口的实现类,拥有接口成员的类:


IRepository

MemoryRepository

ProductTotalizer


接下来,咱们使用 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过滤器、结果过滤器。很容易实现面向切面编程,下降了耦合。
这里举一个我最喜欢的过滤器——对请求的模型进行验证:


 
在控制器或 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.相关引用

相关文章
相关标签/搜索