ASP.NET Core 菜鸟之路:从Startup.cs说起

简介: 1.前言本文主要是以Visual Studio 2017 默认的 WebApi 模板作为基架,基于Asp .Net Core 1.0,本文面向的是初学者,如果你有 ASP.NET Core 相关实践经验,欢迎在评论区补充。

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 往往无从下手,本文将一步步介绍作者的经验,但是不会涉入到内部的代码实现以及相关的原理,那并不是本文想要讨论的范畴。


默认的Startup.cs


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

2.配置参数选项

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

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

虽然提供了很多选择,但是我们只选择其中的JSON文件和环境变量来提供配置参数。

2.1 Json配置参数选项

参考官方文档的示例,我们在 appsettings.json 加入如下的参数:


appsettings.json


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


MyOptions.cs

思考一下 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注册服务以及简单使用

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


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.相关引用



本文采用 知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议
转载请注明来源: 张蘅水
我在开发者头条中还会每日分享不错的技术文章,搜索 356194 即可查看
目录
相关文章
|
1月前
|
Cloud Native API C#
C#的现代化:.NET Core引领的技术革命
【6月更文挑战第9天】`.NET Core引领C#现代化,实现跨平台革命,提升性能并支持云原生应用。异步编程模型优化体验,统一API简化开发流程。C#应用场景扩展,开发效率提高,技术创新加速,预示其未来在技术领域将持续发挥关键作用。`
34 10
|
7天前
|
开发框架 .NET API
.NET Core 和 .NET 标准类库项目类型有什么区别?
在 Visual Studio 中,可创建三种类库:.NET Framework、.NET Standard 和 .NET Core。.NET Standard 是规范,确保跨.NET实现的API一致性,适用于代码共享。.NET Framework 用于特定技术,如旧版支持。.NET Core 库允许访问更多API但限制兼容性。选择取决于兼容性和所需API:需要广泛兼容性时用.NET Standard,需要更多API时用.NET Core。.NET Standard 替代了 PCL,促进多平台共享代码。
|
14天前
|
开发框架 JSON .NET
|
18天前
|
开发框架 .NET Nacos
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
42 0
|
19天前
|
存储 JSON NoSQL
技术心得记录:在.NETCore中使用CSRedis
技术心得记录:在.NETCore中使用CSRedis
13 0
|
20天前
|
SQL 开发框架 .NET
(20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)
(20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)
|
1月前
|
开发框架 .NET Linux
【.NET Developer】已发布好的.NET Core项目文件如何打包为Docker镜像文件
该文介绍了如何不使用VS2019手动创建ASP.NET Core Blazor项目的Dockerfile并构建Docker镜像。首先,创建名为Dockerfile的文件,并复制提供的Dockerfile内容,该文件指定了基础镜像和工作目录。然后,通过CMD在项目目录下运行`docker build -t 自定义镜像名 .`来生成镜像。最后,使用`docker run`命令启动容器并验证项目运行。此外,文章还提到了将镜像推送到Azure Container Registry (ACR)的步骤。
|
1月前
|
Linux C# C++
【.NET Developer】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
本文介绍了如何使用VS2019和.NET框架创建一个Blazor应用,并将其部署到Azure应用服务。首先,Blazor是一个使用C#而非JavaScript构建交互式Web UI的框架,支持共享服务器和客户端应用逻辑,以及与Docker和Azure集成。任务包括创建Blazor项目,配置Dockerfile为Linux容器,本地测试,发布到Azure Container Registry (ACR),然后在Azure App Service for Container上部署。在部署过程中,需确保Docker设置正确,开启ACR的Admin访问权限,并监控镜像拉取和容器启动日志。
|
2月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
114 0
|
2月前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
44 0