.NET Core多平台开发体验[1]: Windows
微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的.NET程序员,相信传统的.NET应用的开发方式已经深深地烙印在你的脑子里面。.NET Core打来了全新的开发体验,但是开发方式的差异根本不足以成为你快速跨入.NET Core 世界的门槛,因为在.NET Core在很多方面比传统的.NET Framework应用开发要简单。为了消除很多尚未接触过.NET Core的读者对未知世界的恐惧,我们先通过几个简单的Hello World应用让大家感受一下.NET Core全新的开发体验。
目录
一、安装开发环境
二、利用命令行创建.NET Core程序
三、改造成一个ASP.NET Core应用
四、进一步改造成ASP.NET Core MVC应用
不管你是否已经接触过.NET Core,我们相信你一定知道.NET Core是“跨平台”的。较之传统的.NET Framework应用只能运行在微软自家的Windows平台上,经过全新设计的.NET Core在诞生的时候就被注入了跨平台的基因,通过.NET Core应用在无需经过任何更改的情况下就可以直接运行在Windows、Mac OS以及各种Linux Distribution(RHEL、Ubuntu、Debian、Fedora、CentOS和SUSE等)平台上。除此之外,.NET Core针对Docker也提供了原生的支持,一个.NET Core应用可以同时运行在Windows Container和Linux Container上。我们接下里现在Windows平台下感受一下.NET Core的开发体验,不过在这之前先得构建一下开发环境。
一、安装开发环境
.NET Core的官方站点(https://www.microsoft.com/net/core)提供了在各种平台下安装开发环境的介绍。总的来时,我们在不同的平台下开发.NET Core应用都需要按照相应的SDK和IDE。针对Windows开发平台来说,.NET Core 2.0.0 SDK可以通过上述这个站点直接下载。成功安装SDK之后,我们在本地将自动拥有了.NET Core的运行时(CoreCLR)、基础类库以及相应的开发工具。
dotnet.exe是.NET Core SDK为我们提供的一个重要的命令行工具,我们在进行.NET Core应用的开发部署的时候将会频繁地使用它。dotnet.exe提供了很多有用的命令,为了不“节外伸枝”,我们就不对它们作系统介绍了,如果后续章节涉及到相关命令的时候,我们再对它们作针对性的介绍。当.NET Core SDK安装结束之后,我们可以运行dotnet命令来确认SDK是否安装成功。如下图1所示,我们执行dotnet --info命令查看当前安装的.NET Core SDK的基本信息,其中包含SDK的版本、运行时环境和共享框架宿主的版本信息。
高效的开发自然离不开一个优秀的IDE,在这方面作为一个.NET程序员是幸福的,因为我们拥有宇宙第一个开发神器Visual Studio。虽然Visual Studio Code也不失为一个优秀的IDE,如果在绝大部分情况下Windows还是主要的开发环境,我个人还是推荐使用Visual Studio。当我们在敲这行文字的时候,Visual Studio的最新版本为2017(15.3)。顺便说一下,Visual Studio已经提供了Mac版本。
Visual Studio Code是一个完全免费并且提供全平台支持(Windows、Mac和Linux)的IDE,我们可以直接在其官网(https://code.visualstudio.com/)上下载。Visual Studio 2017提供了社区版(Community)、专业版(Professional)和企业版(Enterprise),其中社区版是免费的,专业版和企业版需要付费购买。Visual Studio的官网地址为https://www.visualstudio.com/。
除了Visual Studio和Visual Studio Code,我们还可以一款叫做Rider的IDE来开发.NET Core应用。Rider是著名的JetBrains公司开发的一款专门针对.NET的IDE,我们可以利用它来开发ASP.NET、.NET Core、Xmarin以及Unity应用。和Visual Studio Code一样,Rider同样也是个跨平台的IDE,我们可以同时在Windows、Max OS X以及各种桌面版本的Linux Distribution上使用它。不过这不是一款免费的IDE,对它感兴趣的朋友可以在官方站点(https://www.jetbrains.com/rider/)下载30天试用版。
二、利用命令行创建.NET Core程序
通过.NET Core SDK在本地安装的dotnet 工具提供了基于预定义“脚手架(Scaffolding)”创建初始应用的命令(new)。如果需要开发某种类型的.NET Core应用,我们一般不会从第一行代码写起,而是利用这个命令帮助我们创建一个具有初始结构的应用。除此之外,在开发过程中如果需要添加某种类型的文件(比如各种类型的配置文件、MVC的视图文件等),我们也可以利用该命令来完成,通过这种方式添加的文件具有预定义的初始内容。.NET Core SDK在安装的时候为我们提供了一系列预定义的脚手架模板,我们可以按照如下图所示的方式执行命令行“dotnet new list”列出当前安装的脚手架模板。
上图列出就是NET Core 2.0.0 SDK安装后提供的16个预定义的脚手架模板,这些模板大致分为Project Template和Item Template两种类,前者为我们创建一个初始项目,后者则在一个现有项目中针对某种项目元素添加一个或者多个对应的文件。对于细心的读者,可以从上图中看到dotnet new命令具有一个--type参数,该参数具有三个预定义的选项(project、item和other),其中前两个分别对应着Project和Item这两种模板类型。
如果这些预定义的脚手架模板不能满足我们的需求,我们还可以根据自身的需要创建自定义的Project或者Item模板,至于自定义模板的该如何定义,我们就不在这里赘言介绍了,有兴趣的读者朋友可以参考.NET Core官方文档(https://docs.microsoft.com/en-us/dotnet/core/tools/custom-templates)。我们创建的自定义模板最终体现为一个NuGet包,我们可以通过执行dotnet new -i或者dotnet new --install命令对其进行安装。除此之外,对应已经安装的模板,我们可以通过执行dotnet new -u或者dotnet new --uninstall命令卸载。
接下来我们利用dotnet new命令(dotnet new console -n helloworld)按照如下图所示的方式创建一个名为“helloworld”的控制台程序。和传统的.NET Framework应用,一个针对C#的.NET Core项目依然由一个对应的.csproj文件来定义,下图所示的helloworld.csproj就是这么一个文件。
对于传统的.NET Framework应用来说,即使是一个空的C#项目,定义该项目的.csproj文件在内容和结构上显得比较复杂。这个.csproj文件的结构并不是为一般的开发者设计的,我们也不会直接编辑这个文件,而是利用Visual Studio间接地修改它。但是对于一个.NET Core应用来说,这个.csproj文件的结构变得相对简单并清晰了一些,以至于一般的开发人员可以直接编辑它。对于上面我们执行脚手架命令创建的控制台程序,定义项目的这个helloworld.csproj文件的完整内容如下。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> </Project>
如上面的代码片段所示,这个helloworld.csproj是一个根节点为<Project>的XML文件,与项目相关的属性可以根据分组定义在相应的<PropertyGroup>节点下。对于这个helloworld.csproj文件来说,它实际上只定义了两个属性,分别是通过<OutputType>和<TargetFramework>节点表示的编译输出类型和目标框架类型。由于我们创建的是一个针对.NET Core 2.0的可执行控制台应用,所以目标框架为“netcoreapp2.0”,编译输出为Exe(对于Self Contained发布模式)。
我们执行的dotnet new命令行除了帮助我们创建一个空的控制台程序之外,还会帮助我们生成一些初始化代码,这就是项目目录下的这个Program.cs文件的内容。如下所示的代码片段给出了定义在这个文件的整个C#代码的定义,我们可以看到它定义了代表程序入口点的Main方法,并在这个方法中将字符串“Hello World”打印在控制台上。
using System; namespace helloworld { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
虽然很简单,但是我们通过执行脚手架命令行创建出来的是一个完成的.NET Core控制台应用,所以我们可以在无需对其作任何修改的情况下直接对它进行编译和运行,针对.NET Core应用的编译和运行同样是利用这个dotnet.exe命令行来完成的。如下图所示,在进入当前项目所在目录之后,我们执行dotnet build命令对这个控制台应用实施编译,由于默认采用Debug编译模式,所以编译生成的程序集(helloworld.dll)会保存在\bin\debug\目录下。除此之外,针对不同目标框架编译生成的程序集是不同的,所以最终生成的程序集会采用基于目标框架的目录结构进行组织,所以最终生成的这个程序集被保存在“\bin\Debug\netcoreapp2.0\”目录下。
编译后的控制台程序可以直接通过执行dotnet run命令运行。如下图所示,当我们在项目目录下执行dotnet run命令后,编译后的程序随被执行,程序入口Main方法中指定的“Hello World”字符串被直接打印在控制台上。其实当我们执行dotnet run命令启动程序之前无需显示执行dotnet build对源代码实施编译,因为该命令会自动触发编译操作。
三、改造成一个ASP.NET Core应用
我们在上面利用dotnet new命令创建了一个简单的控制台程序,接下来我们将它改造成一个ASP.NET Core应用。一个ASP.NET Core应用构建在ASP.NET Core框架之上,后者利用一个管道式的构建完成了对HTTP请求的监听、接收、处理和最终的响应。ASP.NET Core管道由一个服务器和若干中间件构成,当宿主(Host)程序启动之后,该管道被成功构建出来并利用服务器开始HTTP请求的监听。
从编程层面来看,ASP.NET Core管道的构建主要涉及WebHost和WebHostBuilder这两个对象。我们可以根据命名将WebHost理解为Web应用的宿主,WebHost是由WebHostBuilder构建出来的。一般来说,我们会先创建一个WebHostBuilder对象,并将最终管道构建所需的各项设置通过相应的方法(绝大部分是扩展方法)注册到它上面。注册完成之后,我们直接利用这个WebHostBuilder创建出对应的WebHost,当后者被启动的时候,整个管道会根据我们预定义的设置被构建出来。
接下来我们直接利用Visual Studio 2017打开上面这个helloworld.csproj项目文件。为了在程序性中使用到上述这两个对象,我们自然先得具有对应程序集的引用。对于.NET Core应用来说,所有的程序集都会封装到相应的NuGet包中进行分发,如果需要消费某个框架或者类库,我们都需要预先安装相应的NuGet包。至于NuGet包的安装,我们有很多的方式可以选择。
安装NuGet包
WebHostBuilder所在的程序集包含在Microsoft.AspNetCore.Hosting这个NuGet包中,接下来我们就以它为例介绍若干中不同的NuGet包的安装方式。如果使用Visual Studio 2017来开发.NET Core应用,我们最常使用的是由IDE提供的可视化NuGet安装方式。如果按照这种安装方式,我们只需要在“解决方案管理器(Solution Explorer)”窗口中右键选择对应的项目或者项目下的“依赖(Dependencies)”节点,并在弹出的上下文菜单中选择“管理NuGet包(Manage NuGet Packages)”选项就可以打开如下图所示的“NuGet包管理窗口”。
如上图所示,“NuGet包管理窗口”具有三个标签页,其中“安装(Installed)”和“更新(Updates)”会列出当前项目已经安装和可以升级的NuGet包。如果我们需要按照一个新的NuGet包,我们需要选择第一个标签页,并在左上角的文本框中输入需要安装的NuGet包的全名或者全名的部分文字,与之相关的NuGet包将会筛选出来,如果目标NuGet包正好在该列表中,我们只需要选择它并点击右侧的“安装(Install)”按钮即可。
除了采用上述这种完全可视化的方式来安装NuGet包,Visual Studio还提供了一种命令行的安装方式。包括安装在内的NuGet包管理命令是在Visual Studio的“包管理器控制台(Package Manager Console)”窗口中输入并执行的,我们可以通过菜单“工具(Tools)>选项(Options)>NuGet包管理器(NuGet Package Manager)>包管理器控制台(Package Manager Console)”开启如下图所示的这个窗口。如下图所示,我们通过输入并执行NuGet包安装命令“Install-Package Microsoft.AspNetCore.Hosting -Version 2.0.0”安装版本为2.0.0的NuGet包“Microsoft.AspNetCore.Hosting”。
除了上面介绍的这两种在Visual Studio开发环境中提供的NuGet包的安装放之外,我们已经很熟悉的这个dotnet命令行工具同样提供了安装NuGet包的支持。具体来说,我们可以执行dotnet add package命令安装指定的NuGet包。如下图所示,在确保当前为目标项目所在目录的前提下,我们所需的NuGet包同样可以通过执行“dotnet add package Microsoft.AspNetCore.Hosting --version 2.0.0”命令来完成。
有的情况下我们往往只知道某个需要使用的类型名称而忘记了所在NuGet包的名字,如果你使用了Visual Studio 2017,可以借助它提供的智能提示功能来安装对应的NuGet包。很多人都体现过Visual Studio针对命名空间的自动补齐特性,当我们在C#编辑窗口直接输入一个尚未导入命名空间的类型名称的时候,一旦我们将鼠标落在该类型上面的时候,Visual Studio会自动出现如下图所示的“灯泡”图标,点击该图标之后会出现一组候选的命名空间(这组候选命名空间菜单也可以通过快捷键Ctrl+Alt+F10开启)。
这个特性在Visual Studio 2017上做了进一步改进。如果我们输入了某个类型的名称,但是所在的NuGet包尚未被安装,Visual Studio可以利用这个特性智能地提示这个缺失的NuGet包的名称。如下图所示,在尚未安装“Microsoft.AspNetCore.Hosting”这个NuGet包的情况下,我们在C#编辑窗口中输入WebHostBuilder这个类型,Visual Studio会利用类似的特性提示我们安装缺失的NuGet包。
对于对上述的众多NuGet包的安装方式,它们最终的目的实际上就是在描述当前项目的.csproj问文件中添加一个针对指定NuGet包的引用而已。如下所示的代码片段代表“Microsoft.AspNetCore.Hosting”这个NuGet包被成功安装后的内容,可以看出针对某个NuGet包的引用总是对应着.csproj文件中的某个< PackageReference>节点。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" /> </ItemGroup> </Project>
既然安装NuGet包的最终目的就是在所在项目的.csproj文件中添加一条对应的< PackageReference>节点,那么我们完全可以直接通过修改此文件的方式来完整针对NuGet包的安装。在过去,如果我们想直接利用Visual Studio编辑某个项目对应的.cspro文件,我们必须先采用如下图(左图和中图)的方式将对应的项目卸载。对于.NET Core应用开发来说,直接编辑项目文件已经变成了意见司空见惯的事情,所以Visual Studio 2017允许我们按照下图(右图)所示的方式直接对项目文件进行编辑。
对于.NET Core来说,提供API的程序集总是通过相应的NuGet包来提供,所以NuGet包的安装成为了我们最为频繁的操作之一。除此之外,我们在IDE上也有了更多的选择,所以微软提供了众多NuGet包的管理方式供我们在不同的开发环境中选择。综上所述,我们可以通过如下的方式进行NuGet包的安装:
- 利用Visual Studio的NuGet包管理器(NuGet Package Manager)进行可视化安装。
- 在Visual Studio提供的包管理器控制台(Package Manager Console)以命令行的方式安装NuGet包。
- 通过执行dotnet add package以命令行的形式安装NuGet包。
- 通过修改定义项目的.csproj文件安装的方式安装NuGet包。
在介绍了如何安装NuGet包之后,我们回到本行最初的话题:如何将通过脚手架命令创建的控制台应用转化成一个ASP.NET Core应用。我们在前面已经提到过,ASP.NET Core应用建立在由一个服务器和若干中间件组成的管道上,最初对HTTP请求的监听以及最终对该请求的响应都由服务器完成。微软为我们提供了若干原生的服务器供我们选择,如果对应用具有跨平台的要求,我们可以选择一个名为KestrelHttpServer的服务器。该服务器类型定义在NuGet包“Microsoft.AspNetCore.Server.Kestrel”中,所以我们还需要安装这个NuGet包。
注册服务器与中间件
在安装了所需的NuGet包(“Microsoft.AspNetCore.Hosting”和“Microsoft.AspNetCore.Server.Kestrel”),我们对定义在Program.cs中的Main方法做了如下的改造,这应该算是一个最为简单的ASP.NENT Core应用了。我们首先创建了一个WebHostBuilder对象并调用其扩展方法UseKestrel注册了上述的这个类型为KestrelHttpServer的服务器。接下来我们调用了WebHostBuilder的Configure方法,该方法接受一个Action<IApplicationBuilder>类型的委托对象作为参数,我们利用这个委托对象调用IApplicationBuilder的Run方法注册了一个中间件,后者从事的唯一操作就是在响应中写入了一个内容为“Hello World”的字符串。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; namespace helloworld { class Program { static void Main() { new WebHostBuilder() .UseKestrel() .Configure(app => app.Run( async context => await context.Response.WriteAsync("Hello World"))) .Build() .Run(); } } }
在将所需的服务器和中间件注册到创建的WebHostBuilder对象之后,我们调用其Build方法创建了对应的WebHost对象, 当我们调用后者的Run方法之后,真正的ASP.NET Core管道被构建出来。由于注册的KestrelHttpServer服务器默认采用5000作为监听端口,所以程序在启动之后将绑定在这个端口监听抵达的HTTP请求。如果利用浏览器访问http://localhost:5000,我们会得到由注册中间件写入的“Hello World字符串”。
如果默认使用的5000端口不可用,或者不希望使用这个默认的端口,我们还可以调用WebHostBuilder的扩展方法UseUrls注册新的监听地址。值得一提的是,我们可以同时指定多个监听地址,下面的代码片段就为KestrelHttpServer分别注册了两个端口号分别为6666和8888的监听地址。
new WebHostBuilder() .UseUrls("http://localhost:6666", "http://localhost:8888") .UseKestrel() .Configure(app => app.Run(async context => await context.Response.WriteAsync("Hello World"))) .Build() .Run();
四、进一步改造成ASP.NET Core MVC应用
对于我们在上面一节创建的这个极简ASP.NET Core应用来说,它对应的管道由一个服务器和一个中间件组成,前者的类型为KestrelHttpServer,后者则将每个请求的响应内容统一设置为“Hello World”字符串。接下来我们对该应用做进一步地改造,将它转变成一个ASP.NET Core MVC应用。整个ASP.NET Core MVC框架建立在一个名为RouterMiddleware的中间件上,它利用该中间件提供的路由功能实现了请求URL与目标Controller类型以及Action方法之间的映射,并再此基础上实现了诸如Controller的激活、Action方法的执行以及View的呈现等操作。
整个ASP.NET Core MVC框架实现在“Microsoft.AspNetCore.Mvc”这个NuGet包中,所以我们先得将它安装到我们的控制台项目上,然后才能进行针对ASP.NET Core MVC的编程。不过在这之前我们需要了解一个关于“依赖注入(Dependency Injection)”。ASP.NET Core框架内置了一个原生的依赖注入框架,后者利用一个DI容器提供管道在构建以及进行HTTP请求处理过程中所需的所有服务,而这些服务需要在应用启动的时候被预先注册。对于ASP.NET Core MVC框架来说,它在处理HTTP请求的过程中所需的一系列服务同样需要预先注册。对这个概念有了基本的了解之后,相信读者朋友们对如下所示的代码就容易理解了。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace helloworld { class Program { static void Main(string[] args) { new WebHostBuilder() .UseKestrel() .ConfigureServices(svcs => svcs.AddMvc()) .Configure(app => app.UseMvc()) .Build() .Run(); } } public class HelloController { [HttpGet("/hello")] public string SayHello() { return "Hello World"; } } }
如果说上面一节创建的属于最简单的ASP.NET Core应用的话,那么通过上面这段代码创建的恐怕就是一个简单的ASP.NET Core MVC应用了。我们在这个程序中调用了WebHostBuilder的方法ConfigureServices(这是一个扩展方法,其参数类型为Action<IServiceCollection>)注册ASP.NET Core MVC框架所需的服务,具体来说这些服务是通过调用IServiceCollection接口的扩展方法AddMvc来完成的。在针对Configure方法的调用中,我们调用IApplicationBuilder的扩展方法UseMvc注册了RouterMiddleware中间件以及针对ASP.NET Core MVC的路由处理器,后者接收路由分发的HTTP请求和路由参数,并在此基础上实现对Controller的激活、Action方法的执行以及View的呈现等操作。
HelloController是我们定义的Controller类型。按照约定,所有的Controller类型名称都应该以“Controller”字符作为后缀。与之前版本的ASP.NET MVC不同的是,ASP.NET Core MVC下的Controller类型并不要求强制继承某个基类。在HelloController中,我们定义了一个唯一无参Action方法SayHello,该方法直接返回一个内容为“Hello World”的字符串。我们在这个方法上利用HttpGetAttribute注册了一个模板为“/hello”的路由,意味着请求地址为“/hello”的GET请求最终会被路由到这个Action方法上,后者执行的结果将作为请求的响应内容。所以启动该程序后使用浏览器访问地址“http://localhost:5000/hello”,我们依然会得到如图1-13所示的输出结构。
引入View
上面这个程序并没有涉及View,所以算不上一个典型的ASP .NET Core MVC,接下来我们对它做进一步改造。在前面介绍如何安装NuGet包的时候,我们曾经查看过定义项目的 .csproj文件的内容,实际上这是一个以<Project>作为根节点的XML文件。作为根节点的<Project>元素具有一个Sdk的属性表示当前项目针对的SDK,不同的SDK在编译发布等方面对项目提供了不同的支持。当我们安装了.NET Core SDK的时候,这些针对项目的SDK将被安装在“%programfiles% \dotnet\sdk\{version}\Sdks”目录下(如下图所示)。
对于上面这个通过脚手架命令行创建的控制台应用来说,它默认采用的SDK为“Microsoft.NET.Sdk”。如果我们需要为这个应用添加View,这个SDK并不能提供针对View的动态编译功能(在运行时动态编译使用到的View,而不是在部署的时候将所有View进行预编译),所以我们按照如下的方式编辑.csproj文件将SDK切换为“Microsoft.NET.Sdk.Web”。实际上不论是我们利用Visual Studio还是命令行创建的ASP.NET Core应用,项目都会采用这个SDK。
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" /> </ItemGroup> </Project>
在对SDK作了相应修改之后,我们对现有的程序作了如下的改造。当编译器在对View进行动态编译的时候,需要按照预定义的路径去定位定义View的.cshtml文件,这些预定义的候选路径都是相对路径,所以我们需要预先指定一个基础路径,该路径可以通过调用WebHostBuilder的扩展方法UseContentRoot来指定。如下面的代码片段所示,我们将当前路径(当前项目的根目录)作为这个基础路径。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using System.IO; namespace helloworld { class Program { static void Main() { new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .ConfigureServices(svcs => svcs.AddMvc()) .Configure(app => app.UseMvc()) .Build() .Run(); } } public class HelloController: Controller { [HttpGet("/hello/{name}")] public IActionResult SayHello(string name) { ViewBag.Name = name; return View(); } } }
为了让HelloController具有View呈现的能力,我们让它派生于基类Controller。Action方法SayHello的返回类型被修改为IActionResult接口,后者表示Action方法执行的结果。除此之外,我们为该方法定义了一个表示姓名的参数name,通过HttpGetAttribute特性注册的路由模板(“/hello/{name}”)中具有与之对应的路由参数。换句话说,满足该路由规则的请求URL携带的命名将自动绑定为该Action方法的name参数。具体在SayHello方法中,我们利用ViewBag将代码姓名的name参数的值传递给将要呈现出来的View,该View正式执行View方法返回的结果。
由于我们调用View方法时并没有显式指定名称,ASP.NET Core MVC的View引擎会自动将当前Action的名称(“SayHello”)作为View的名称。如果该View还没有经过编译(部署时针对View的预编译,或者在这之前针对该View的动态编译),View引擎将从若干候选的路径中读取对应的.cshtml 文件进行编译,其中首选的路径为“{ContentRoot}\Views\{ControllerName}\{ViewName}.cshtml”。为了迎合View引擎定位View文件的规则,我们需要将SayHello对应的View文件(SayHello.cshtml)定义在目录“\Views\Hello\”下(如下图所示)。
如下所示的就是SayHello.cshtml这个文件的内容,这是一个针对Razor引擎的View文件。从文件的扩展名(.cshtml)我们可以这样的文件可以同时包含HTML标签和C#代码。总的来说,View文件最终是为了在服务端渲染出最终在浏览器呈现出来的HTML,我们可以在这个文件中直接编写原样输出的HTML标签,也可以内嵌一段动态执行的C#代码。虽然Razor引擎对View文件的编写制定了严格的语法,但是我个人觉得没有必要在Razor语法上花太多的精力,因为Razor语法的目的就是让我们很“自然”地融合动态C#代码和静态HTML标签来定义最终在客户端渲染的HTML文档,因此它的语法和普通的思维基本上是一致。比如下面这个View最终会生成一个完整的HTML文档,其主体部分只有一个<h3>标签。该标签的内容是动态的,因为包含从Controller利用ViewBag传进来的姓名。
<html> <head> <title>Hello World</title> </head> <body> <h3>Hello, @ViewBag.Name</h3> </body> </html>
再次运行该程序后,我们利用浏览器访问地址“http://localhost:5000/hello/foobar”。由于请求地址与Action方法SayHello上的路由规则相匹配,所以URL上的foobar会被解析为姓名绑定到该方法的name参数上,所以我们最终将在浏览器上得到如下图所示的输出结果。
利用Startup注册服务和中间件
对于几乎所有的ASP.NET Core应用来说,应用启动过程中必须完成的初始化操作都包括服务与中间件的注册。在上面演示的实例中,我们都是直接调用WebHostBuilder的ConfigureServices和Configure方法来完成针对服务和中间件的注册,但是在大部分真实的开发场景中,我们一般会将这两种类型的注册定义在一个单独的类型中。按照约定,我们通常会将这个类型命名为Startup,比如我们演示实例中针对ASP.NET Core MVC的服务注册和中间件注册就可以放在如下定义的这个Startup类中。
public class Startup { public void ConfigureServices(IServiceCollection svcs) { svcs.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } }
如上面的代码片段所示,我们不需要让Startup类实现某个预定义的接口或者继承某个预定义基类,所采用的完全是一种“约定”,随着对ASP.NET Core框架认识的加深,我们会发现这种“约定优于配置”的设计广泛地应用在整个框架之中。按照约定,服务注册和中间件注册分别实现在ConfigureServices和Configure方法中,它们第一个参数类型分别为IServiceCollection和IApplicationBuilder接口。现在已经将两种核心的注册操作转移到了上面这个Startup类中,那么我们需要将该类型按照如下的方式调用UseStartup<T>方法注册到WebHostBuilder上即可。
new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .UseStartup<Startup>() .Build() .Run();
.NET Core多平台开发体验
.NET Core多平台开发体验[1]: Windows
.NET Core多平台开发体验[2]: Mac OS X
.NET Core多平台开发体验[3]: Linux
.NET Core多平台开发体验[4]: Docker