ASP.NET Core 3 框架揭秘(上下册)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.1 Windows平台

“跨平台”是.NET Core区别于传统.NET Framework最核心的特征。相较于传统的.NET Framework应用只能运行在微软的 Windows平台上,经过全新设计的.NET Core在诞生的时候就被注入了跨平台的“基因”。.NET Core应用在无须经过任何更改的情况下就可以直接运行在Windows、macOS以及各种 Linux(RHEL、Ubuntu、Debian、Fedora、CentOS和 SUSE等)平台上。除此之外,.NET Core针对容器也提供了原生的支持,一个.NET Core应用可以同时运行在Windows Container和Linux Container上。下面先介绍Windows平台上.NET Core应用的开发体验。

1.1.1 构建开发环境

.NET Core 的官方站点介绍了在各种平台上构建开发环境的方式。总的来说,在不同的平台上开发.NET Core应用都需要安装相应的SDK和IDE。成功安装SDK之后,我们在本地将自动拥有.NET Core的运行时(CoreCLR)、基础类库以及相应的开发工具。

dotnet.exe是.NET Core SDK提供的一个重要的命令行工具,在进行.NET Core应用的开发部署时会频繁地使用 dotnet.exe。dotnet.exe 提供了很多有用的命令,此处不做系统介绍,如果后续章节涉及相关命令,我们再对它们做针对性的介绍。当.NET Core SDK 安装结束之后,通过运行“dotnet”命令可以确认 SDK 是否安装成功。如图 1-1 所示,执行“dotnet--info”命令可以查看当前安装的.NET Core SDK的基本信息,显示的信息包含SDK的版本、运行时环境以及本机安装的所有运行时版本。

图1-1 执行“dotnet--info”命令获取.NET Core SDK(Windows)的基本信息

高效的开发离不开优秀的 IDE,在这方面作为一个.NET 开发者是幸福的,因为我们拥有Visual Studio。虽然Visual Studio Code也是一个优秀的IDE,但Windows依旧是主要的开发环境,所以笔者推荐使用 Visual Studio。截至 2019 年,Visual Studio 的最新版本为 2019。另外,Visual Studio已经提供了Mac版本。

Visual Studio Code是一个完全免费并且提供全平台(Windows、Mac和Linux)支持的IDE,读者可以直接在其官网上下载。Visual Studio 2019 提供了社区版(Community)、专业版(Professional)和企业版(Enterprise),其中社区版是免费的,专业版和企业版需要付费购买。

除了 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以及各种桌面版本的Linux Distribution上使用它。但Rider不是一款免费的IDE,对其感兴趣的读者可以在官方站点下载30天试用版。

1.1.2 利用命令行创建.NET Core应用

dotnet.exe提供了基于“脚手架”(Scaffolding)创建初始应用的new命令。如果需要开发某种类型的.NET Core应用,我们一般不会从第一行代码开始写,而是利用这个 new命令创建一个具有初始结构的应用程序。除此之外,在开发过程中如果需要添加某种类型的文件(如各种类型的配置文件、MVC 的视图文件等),我们也可以利用该命令来完成,通过这种方式添加的文件具有预定义的初始内容。.NET Core SDK 在安装时提供了一系列预定义的脚手架模板,我们可以按照图1-2所示的方式执行“dotnet new--list”命令列出当前安装的脚手架模板。

图1-2 执行“dotnet new--list”命令获取脚手架模板列表

图 1-2列出的就是安装.NET Core SDK后提供的预定义的脚手架模板,这些模板大致分为Project Template和Item Template两类,前者为我们创建一个初始项目,后者则在一个现有项目中针对某种项目元素添加一个或者多个对应的文件。细心的读者可以从图1-2中看到dotnet new命令具有一个--type参数,该参数具有 3个预定义的选项(project、item和 other),其中,前两个预定义的选项分别对应Project和Item这两种模板类型。

如果这些预定义的脚手架模板无法满足我们的需求,还可以创建自定义的 Project或者 Item模板,至于自定义模板应该如何定义,有兴趣的读者可以参考.NET Core官方文档。自定义模板最终会封装成一个NuGet包,我们可以通过执行“dotnet new--i”命令或者“dotnet new--install”命令对其进行安装。除此之外,对于已经安装的模板,我们可以通过执行“dotnet new--u”命令或者“dotnet new--uninstall”命令将其卸载。

下面执行“dotnet new”命令(dotnet new console-n helloworld),按照图1-3所示的方式创建一个名为“helloworld”的控制台程序。和传统的.NET Framework应用一样,一个针对C#语言的.NET Core项目依然由一个对应的.csproj文件进行定义,图1-3中的helloworld.csproj就是这样的一个文件。

图1-3 执行“dotnet new”命令创建一个控制台程序

对于传统的.NET Framework 应用来说,即使是一个空的 C#语言项目,定义该项目的.csproj 文件在内容和结构上都是很复杂的,因为这个.csproj 文件的结构并不是面向开发者设计的,我们也不会直接编辑这个文件,而是利用 Visual Studio 通过设置当前项目的某些属性间接地修改它。但是对于一个.NET Core 应用来说,这个.csproj 文件的结构变得相对简单并且更加清晰,所以开发人员经常会直接编辑它。对于前面我们执行脚手架命令创建的控制台程序,定义项目的helloworld.csproj文件的完整内容如下。

如上面的代码片段所示,这个 helloworld.csproj是一个根节点为<Project>的 XML文件,与项目相关的属性可以分组定义在相应的<PropertyGroup>节点下。这个helloworld.csproj文件实际上只定义了两个属性,分别是通过<OutputType>节点和<TargetFramework>节点表示的编译输出类型与目标框架。由于我们创建的是一个针对.NET Core 3.0的可执行控制台应用,所以目标框架为netcoreapp3.0,编译输出为Exe。

dotnet new命令行除了可以创建一个空的控制台程序,还可以生成一些初始化代码,这就是项目目录下的 Program.cs文件的内容。如下所示的代码片段给出了定义在这个文件中的整个 C#代码的定义,同时定义了代表程序入口点的 Main 方法,并且在这个方法中将字符串“Hello World!”打印在控制台上。

通过执行脚手架命令行创建的应用程序虽然简单,但它是一个完整的.NET Core 应用,可以在无须任何修改的情况下直接编译和运行。针对.NET Core 应用的编译和运行同样是执行“dotnet.exe”命令行完成的。如图 1-4所示,在进入当前项目所在目录之后,可以执行“dotnet build”命令对这个控制台应用实施编译,由于默认采用 Debug 编译模式,所以编译生成的程序集会保存在“\bin\Debug\”目录下。除此之外,针对不同目标框架编译生成的程序集是不同的,由于我们创建的是针对.NET Core 3.0 的应用程序,所以最终生成的程序集被保存在“\bin\Debug\netcoreapp3.0\”目录下。

图1-4 执行“dotnet build”命令编译一个控制台程序

如果查看编译的输出目录,可以发现两个同名(helloworld)的文件,一个是 helloworld.dll,另一个是 helloworld.exe,后者在尺寸上会大很多。另外,helloworld.exe是一个可以直接运行的可执行文件;而 helloworld.dll仅仅是一个单纯的动态链接库,需要借助命令行 dotnet才能执行。

如图 1-5 所示,当我们在项目目录下执行“dotnet run”命令后,编译后的程序随即被执行,程序入口 Main 方法中指定的“Hello World!”字符串被直接打印在控制台上。其实,执行“dotnet run”命令启动程序之前无须显式执行“dotnet build”命令对源代码实施编译,因为该命令会自动触发编译操作。在执行“dotnet”命令启动应用程序集时,我们也可以直接指定启动程序集的路径(“dotnet bin\Debug\netcoreapp3.0\helloworld.dll”)。(S101)[1]

图1-5 执行“dotnet run”命令运行一个控制台程序

1.1.3 ASP.NET Core应用

前面利用 dotnet new 命令创建了一个简单的控制台程序,下面将其改造成一个 ASP.NET Core应用。一个ASP.NET Core应用构建在ASP.NET Core框架之上,ASP.NET Core框架利用一个消息处理管道完成对 HTTP请求的监听、接收、处理和最终的响应(第 11章至第 13章对管道有详细的介绍)。ASP.NET Core 管道由一个服务器(Server)和若干中间件(Middleware)构成,当宿主(Host)程序启动之后,管道被构建出来,作为管道“龙头”的服务器就开始监听来自客户端的HTTP请求。

添加引用

下面直接利用 Visual Studio 打开项目文件 helloworld.csproj。为了能够使用 ASP.NET Core框架提供的程序集,我们可以通过修改项目文件 (.csproj) 添加针对“Microsoft.AspNetCore.App”的框架引用(FrameworkReference)。在 Visual Studio中修改项目文件非常方便,我们只需要右击选择目标项目,并从弹出的菜单中选择“Edit Project File”命令即可。如果没有特殊说明,本书后续章节提供的控制台实例演示基本上添加了针对“Microsoft.AspNetCore.App” 的框架引用。下面是修改后的项目文件,对“Microsoft.AspNetCore.App”的框架引用被添加到<ItemGroup/>节点下。

注册服务器与中间件

从应用承载或者寄宿(Hosting)方面来看,.NET Core具有一个以IHost/IHostBuilder为核心的服务承载系统(第 10 章会对承载系统进行详细介绍),任何需要长时间运行的操作都可以定义成IHostedService 服务并通过该系统来承载。IHost 对象可以视为所有承载服务的宿主(Host),而IHostBuilder对象则是它的构建者(Builder)。一个ASP.NET Core应用本质上就是一个用来监听、接收和处理 HTTP 请求的后台服务,所以它被定义成一个 GenericWebHostService(实现了IHostedService接口),并将它注册到承载系统中,进而实现了针对ASP.NET Core应用的承载。

一个运行的ASP.NET Core应用本质上体现为由一个服务器和若干中间件构成的消息处理管道,服务器解决针对 HTTP 请求的监听、接收和最终的响应,具体针对请求的处理则由它递交给后续的中间件来完成。这个管道是由GenericWebHostService服务构建的。

ASP.NET Core 提供了几种原生的服务类型,比较常用的是 KestrelServer 和 HTTP.sys。KestrelServer是采用libuv创建的跨平台的Web服务器,可以在Windows、macOS和Linux平台上使用。它不仅可以作为独立的Web服务器直接对外提供服务,还可以结合传统的Web服务器(如 IIS、Apache和 Nginx)将它们作为反向代理来使用。HTTP.sys则是一种只能在 Windows平台上使用的 Web 服务器,由于它本质上是一个在操作系统内核模式运行的驱动,所以能够提供非常好的性能。本书所有的实例全部采用KestrelServer。

在对项目文件 helloworld.csproj做了上述修改之后,我们对定义在 Program.cs中的 Main方法做了如下改动:调用静态类型 Host的 CreateDefaultBuilder方法创建了一个 IHostBuilder对象,并最终调用该对象的Build方法构建作为服务宿主的IHost对象。当我们调用IHost对象的Run扩展方法时,ASP.NET Core应用程序将会被启动。

在调用Build方法构建IHost对象之前,可以调用IHostBuilder接口的ConfigureWebHost扩展方法,并利用指定的Action<IWebHostBuilder>委托对象构建ASP.NET Core应用的请求处理管道。具体来说,我们调用IWebHostBuilder接口的UseKestrel扩展方法将KestrelServer注册为服务器,调用 Configure 扩展方法注册了用来处理请求的中间件。Configure 扩展方法的输入参数是一个Action<IApplicationBuilder>对象,所需的中间件注册在IApplicationBuilder对象上。演示程序注册的唯一中间件是通过调用IApplicationBuilder接口的Run扩展方法注册的,该中间件利用指定的Func<HttpContext,Task>对象将响应的主体内容设置为“Hello World.”。

我们可以按照上面演示的程序通过执行“dotnet”命令行启动该程序,也可以直接在 Visual Studio中按F5键或者Ctrl+F5组合键启动该程序。执行“dotnet run”命令启动ASP.NET Core程序,输出结果如图 1-6所示,这些输出其实是通过日志的形式输出的(第 8章和第 9章会对诊断日志进行详细介绍)。从这些输出可以看出 ASP.NET Core 应用采用的默认监听地址(“http//localhost:5000”和“https//localhost:5001”)和承载环境(Production)。如果需要关闭应用程序,按Ctrl+C组合键即可。

图1-6 执行“dotnet run”命令启动ASP.NET Core程序

注册的 KestrelServer会绑定到“http//localhost:5000”和“https//localhost:5001”这两个地址监听请求,如果利用浏览器分别对这两个地址发起请求会得到怎样的响应?如图 1-7 所示,两个请求都会得到主体内容为“Hello World.”的响应(由于证书的问题,Chrome 浏览器为HTTPS的请求会显示“Not secure”的警告),毫无疑问,该内容就是中间件写入的。(S102)

图1-7 “Hello World.”应用的响应

修改SDK

每个.NET Core 应用都针对一种具体的 SDK 类型。前面展示了项目文件 helloworld.csproj 的完整定义,这是一个 XML 文件,在根节点的<Project>上通过 SDK 属性设置了当前项目采用的 SDK类型。前面通过 dotnet new命令工具创建的控制台应用默认采用的 SDK类型为“Microsoft.NET.Sdk”,而 ASP.NET Core 应用通常采用另一种名为“Microsoft.NET.Sdk.Web”的SDK类型。

如果将 SDK设置为“Microsoft.NET.Sdk.Web”,就可以将针对“Microsoft.AspNetCore.App”的框架引用从项目文件中删除。由于不需要利用生成的.exe文件启动ASP.NET Core应用,所以应该将 XML元素<OutputType>Exe</OutputType>从<PropertyGroup>节点中删除。因此,最终的项目文件只需要保留如下内容即可。

launchSettings.json

当我们通过修改项目文件 helloworld.csproj 将 SDK 类型改为“Microsoft.NET.Sdk.Web”之后,如果使用 Visual Studio打开这个文件,一个名为 launchSettings.json的配置文件将自动生成并且被保存在“\Properties”目录下。顾名思义,launchSettings.json 是一个在应用启动时自动加载的配置文件,该配置文件可以在不同的设置下执行应用程序。下面的代码片段就是 Visual Studio 自动创建的 launchSettings.json 文件的内容。由此可以看出,该配置文件默认添加了两个节点,其中,iisSettings节点用于设置IIS相关的选项,而profiles节点定义了一系列用于描述应用启动场景的Profile。

初始的launchSettings.json文件会默认创建两个Profile,一个被命名为“IIS Express”,另一个则使用应用名称命名(“helloworld”)。每个 Profile 相当于定义了应用的启动场景,相关的设置包括应用启动的方式、环境变量和URL等,具体的设置包括以下几点。

commandName:启动当前应用程序的命令类型,有效的选项包括 IIS、IISExpress、Executable 和 Project,前三个选项分别表示采用 IIS、IISExpress 和指定的可执行文件(.exe)来启动应用程序。如果使用“dotnet run”命令启动程序,那么对应 Profile 的启动命名名称应该设置为Project。

executablePath:如果 commandName 属性被设置为 Executable,就需要利用该属性设置启动可执行文件的路径(绝对路径或者相对路径)。

environmentVariables:该属性用来设置环境变量。由于 launchSettings.json文件仅仅在开发环境中使用,所以默认会添加一个名为“ASPNETCORE_ENVIRONMENT”的环境变量,并将它的值设置为“Development”,ASP.NET Core应用就是利用这样一个环境变量来表示当前部署环境的。

commandLineArgs:命令行参数,即传入Main方法的参数列表。

workingDirectory:启动当前应用运行的工作目录。

applicationUrl:应用程序采用的URL列表,多个URL之间用分号(;)进行分隔。

launchBrowser:一个布尔类型的开关,表示应用程序的时候是否自动启动浏览器。

launchUrl:如果 launchBrowser 被设置为 True,浏览器采用的初始化路径就通过该属性进行设置。

nativeDebugging:是否启动本地代码调试(Native Code Debugging),默认值为False。

externalUrlConfiguration:如果该属性被设置为 True,就意味着禁用本地的配置,默认值为False。

use64Bit:如果 commandName属性被设置为 IIS Express,该属性决定采用 X64版本还是 X86 版本,默认值为 False,意味着 ASP.NET Core 应用默认采用 X86 版本的 IIS Express。

launchSettings.json 文件中的所有设置仅仅针对开发环境,在产品环境下是不需要这个文件的,应用发布后生成的文件列表中也不包含该文件。该文件不需要手动编辑,当前项目属性对话框(通过在解决方案对话框中右击选择“Properties”(属性)选项来打开)中“Debug”(调试)选项卡下的所有设置最终都会体现在该文件上(见图1-8)。

图1-8 在Visual Studio中通过设置调试选项编辑launchSettings.json文件

如果在 launchSettings.json 文件中设置了多个 Profile,它们会以图 1-9 所示的形式出现在Visual Studio的工具栏中,我们可以选择任意一个 Profile中定义的配置选项启动当前的应用程序。如果在Profile中通过设置launchBrowser属性选择启动浏览器,我们还可以选择浏览器的类型。

图1-9 在Visual Studio中选择Profile

如果我们在当前项目所在目录下通过执行“dotnet run”命令启动应用程序,launchSettings.json文件就会默认被加载。我们可以通过命令行参数--launch-profile 指定采用的 Profile。如果没有对Profile做显式指定,那么该配置文件中第一个 commandName为“Project”的 Profile会默认被使用。如图 1-10 所示,在创建应用的根目录下通过执行“dotnet run”命令启动应用程序,其中,第一次执行“dotnet run”命令时显式设置了 Profile 名称(--launch-profile helloworld)。从输出结果可以看出,两次采用的是同一个Profile。

图1-10 执行“dotnet run”命令时选择Profile

如果在执行“dotnet run”命令时不希望加载 launchSettings.json文件,可以通过显式指定命令行参数--no-launch-profile 实现。如图 1-11 所示,执行“dotnet run”命令时指定了参数--no-launch-profile,所以应用会采用 KestrelServer 默认的监听地址(“http://localhost:5000”和“https://localhost:5001”)。由于 launchSettings.json 文件没有被加载,所以当前的执行环境从Development变为默认的Production。

图1-11 执行“dotnet run”命令时通过--no-launch-profile参数阻止加载launchSettings.json文件

显式指定URL

如果既不使用launchSettings.json文件中定义的URL,也不使用KestrelServer默认采用的监听地址,我们可以在应用程序中显式指定应用的 URL。正如下面的代码片段所示,只需要调用IWebHostBuilder接口的UseUrls扩展方法指定一组以分号分隔的URL即可。(S103)

ConfigureWebHostDefaults

上面演示的实例,都是通过调用 IHostBuilder 接口的 ConfigureWebHost 扩展方法,借助指定的 Action<IWebHostBuilder>委托对象构建处理请求,来处理管道的。该接口还有一个ConfigureWebHostDefaults 扩展方法,它会做一些默认设置。正如下面的代码片段所示,如果调用这个方法,KestrelServer就不需要进行显式注册。至于 ConfigureWebHostDefaults扩展方法究竟做了哪些默认设置,有兴趣的读者可以参阅第13章。(S104)

1.1.4 ASP.NET Core MVC应用

由于ASP.NET Core框架在本质上就是由服务器和中间件构建的消息处理管道,所以在它上面构建的应用开发框架都建立在某种类型的中间件上,整个 ASP.NET Core MVC开发框架就是建立在用来实现路由的 EndpointRoutingMiddleware中间件和 EndpointMiddleware中间件上的。ASP.NET Core MVC利用路由系统为它分发请求,并在此基础上实现针对目标Controller的激活、Action 方法的选择和执行,以及最终对于执行结果的响应。在介绍的实例演示中,我们将对上面创建的ASP.NET Core做进一步改造,使之转变成一个MVC应用。

注册服务与中间件

ASP.NET Core框架内置了一个原生的依赖注入框架(第3章和第4章会对依赖注入进行系统而深入的介绍),该框架利用一个依赖注入容器提供管道在构建及请求处理过程中所需的服务,而这些服务需要在应用启动的时候被预先注册。对于 ASP.NET Core MVC框架来说,它在处理HTTP 请求的过程中所需的一系列服务同样需要预先注册。对这个概念有了基本的了解之后,读者对如下所示的代码就容易理解了。

整个 ASP.NET MVC框架建立在 EndpointRoutingMiddleware中间件和 EndpointMiddleware中间件构建的路由系统上,这两个中间件采用“终结点(Endpoint)映射”的方式实现针对HTTP请求的路由(由这两个中间件构建的路由系统在第15章有详细介绍)。这里所谓的终结点可以视为应用程序提供的针对 HTTP 请求的处理器,这两个终结点通过预先设置的规则将具有某些特征的请求(如路径、HTTP 方法等)映射到对应的终结点,进而实现路由的功能。对于一个MVC应用程序来说,我们可以将定义在Controller类型中的Action方法视为一个终结点,那么路由映射最终体现在HTTP请求与目标Action方法的映射上。

上面的代码片段先后调用 IApplicationBuilder接口的 UseRouting方法与 UseEndpoints方法注册了 EndpointRoutingMiddleware中间件和 EndpointMiddleware中间件。在调用 UseEndpoints方法的时候,我们利用指定的 Action<IEndpointRouteBuilder>委托对象调用了IEndpointRouteBuilder接口的 MapControllers扩展方法,从而完成了针对定义在 Controller类型中所有Action方法的映射。

由于注册的中间件具有对其他服务的依赖,所以需要预先将这些服务注册到依赖注入框架中。依赖服务的注册是通过调用IWebHostBuilder接口的ConfigureServices方法完成的,该方法的参数类型为Action<IServiceCollection>,添加的服务注册就保存在IServiceCollection接口表示的集合中。在上面的演示程序中,两个中间件依赖的服务是通过调用 IServiceCollection 接口的AddRouting方法和AddControllersWithViews方法进行注册的。

如下所示的 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-7所示。(S105)

引入视图

上面这个程序并没有涉及视图,所以不是一个典型的 MVC 应用,下面对其做进一步改造。为了使 HelloController 具有视图呈现的能力,我们让它派生于基类 Controller。Action 方法SayHello的返回类型被修改为 IActionResult接口,它表示 Action方法执行的结果。可以为该方法定义一个表示姓名的参数 name,通过 HttpGetAttribute特性注册的路由模板(“/hello/{name}”)中具有与之对应的路由参数。换句话说,满足该路径模式的请求 URL携带的姓名,将自动绑定到该 Action 方法的参数 name 上。在 SayHello 方法中,可以利用 ViewBag 将代表姓名的参数name 的值传递给呈现的视图,该方法最终调用 View 方法返回当前 Action 方法对应的ViewResult对象。

由于调用 View 方法时没有显式指定视图的名称,所以视图引擎会将当前 Action 的名称(SayHello)作为视图的名称。如果该视图还没有经过编译(部署时针对 View 的预编译,或者在这之前针对该 View的动态编译),视图引擎将从若干候选路径中读取对应的.cshtml 文件进行编译,其中首选的路径为“{ContentRoot}\Views\{ControllerName}\{ViewName}.cshtml”。为了满足视图引擎定位视图文件的规则,我们需要将 SayHello 对应的视图文件(SayHello.cshtml)定义在目录“\Views\Hello\”下(见图1-12)。

图1-12 View文件所在的路径

如下代码片段就是 SayHello.cshtml 文件的内容,这是一个针对 Razor 引擎的视图文件。从文件的扩展名(.cshtml)可以看出,这样的文件可以同时包含HTML标签和C#代码。总的来说,视图文件会在服务端生成最终在浏览器中呈现出来的 HTML,既可以在这个文件中直接提供原样输出的HTML标签,也可以内嵌一段动态执行的C#代码。虽然Razor引擎对View文件的编写有严格的语法,但是笔者认为没有必要在 Razor 语法上花费太多精力,因为 Razor 语法的目的就是“自然”地将动态 C#代码和静态 HTML标签结合起来,最终生成一份完整的 HTML文档,因此它的语法和普通的思维基本上是一致的。例如,下面的View文件最终会生成一个完整的 HTML 文档,其主体部分只有一个<p>标签。该标签的内容是动态的,这是因为包含利用ViewBag从Controller传进来的姓名。

再次运行该程序后,可以利用浏览器访问地址“http://localhost:5000/hello/foobar”。由于请求地址与 Action 方法 SayHello 上的路由规则相匹配,所以路径携带的姓名(foobar)会绑定到该方法的参数name上,最终的输出结果如图1-13所示。(S106)

图1-13 启动并访问ASP.NET Core MVC应用

注册Startup类型

任何一个ASP.NET Core应用在初始化的时候都会根据请求处理的需求注册对应的中间件。前面演示的实例都是直接调用 IWebHostBuilder接口的 Configure扩展方法注册所需的中间件,但是在大部分真实的开发场景中,我们一般会将中间件及依赖服务的注册定义在一个单独的类型中,按照约定,通常将这个类型命名为 Startup。例如,针对服务和中间件的注册就可以放在如下代码片段定义的Startup类型中。

如上面的代码片段所示,不需要让 Startup 类型实现某个预定义的接口或者继承某个预定义基类,所采用的完全是一种基于“约定”的定义方式。随着对 ASP.NET Core框架认识的加深,可以发现这种“约定优于配置”的设计广泛地应用在整个框架之中。按照约定,服务注册和中间件注册分别在 ConfigureServices 方法与 Configure 方法中实现,它们的第一个参数类型分别为IServiceCollection接口和IApplicationBuilder接口。由于已经将两种核心操作转移到Startup类型中,所以需要注册该类型。Startup类型可以调用 IWebHostBuilder接口的 UseStartup<TStartup>扩展方法进行注册。(S107)

前面对.NET Core、ASP.NET Core及ASP.NET Core MVC应用的编程做了初步介绍,但是这仅仅限于我们熟悉的 Windows 平台。作为一个跨平台的开发框架,我们有必要在其他操作系统平台上体验.NET Core开发的乐趣。