[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

时间:2021-10-07 01:50:10

我们在《上篇》利用dotnet new命令创建了一个简单的控制台程序,接下来我们将它改造成一个ASP.NET Core应用。一个ASP.NET Core应用构建在ASP.NET Core框架之上,ASP.NET Core框架利用一个消息处理管道完成对HTTP请求的监听、接收、处理和最终的响应。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”的框架引用被添加到<ItemGroup/>节点下。

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
</ItemGroup>
</Project>

二、注册服务器与中间件

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

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

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

在对项目文件helloworld.csproj作了上述修改之后,我们对定义在Program.cs中的Main方法做了如下的改造。我们调用了静态类型HostCreateDefaultBuilder方法创建了一个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”。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting; namespace helloworld
{
class Program
{
static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHost(webHostBuilder => webHostBuilder
.UseKestrel()
.Configure(app => app.Run(
context => context.Response.WriteAsync("Hello World."))))
.Build()
.Run();
}
}
}

我们可以按照上面演示的那样通过执行dotnet命令行来启动该程序,也可以直接在Visual Studio中按F5或者Ctrl+F5启动该程序。下图所示的是执行dotnet run命令后控制台的输出结果,这些输出其实是通过日志的形式输出的。我们从这些输出可以看出ASP.NET Core应用采用的默认监听地址(http//localhost:5000和https//localhost:5001)和承载环境(Production)。如果需要关闭应用程序,只需要按Ctrl+C组合键就可以了。

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

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

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

三、修改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>节点中删除,所以最终的项目文件只需要保留如下的内容就可以了。

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
</Project>

四、launchSettings.json

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

{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51127/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"helloworld": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:51128/"
}
}
}

初始的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)”选项卡下的所有设置最终都会体现在该文件上。

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

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

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

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

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

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

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]

五、显式指定URL

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

class Program
{
static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHost(webHostBuilder => webHostBuilder
.UseKestrel()
.UseUrls("http://0.0.0.0:3721;https://0.0.0.0:9527")
.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World."))))
.Build()
.Run();
}
}

六、ConfigureWebHostDefaults

在上面演示的实例中,我们都是调用IHostBuilder接口的ConfigureWebHost扩展方法借助指定的Action<IWebHostBuilder>委托对象来构建处理请求处理管道,该接口还有另一个ConfigureWebHostDefaults的扩展方法,它会为我们作一些默认设置。如下面的代码片段所示,如果调用这个方法,KestrelServer服务器都不需要进行显式注册。

class Program
{
static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World."))))
.Build()
.Run();
}
}

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]
[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [中篇]
[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [下篇]
[ASP.NET Core 3框架揭秘] 跨平台开发体验: Mac OS
[ASP.NET Core 3框架揭秘] 跨平台开发体验: Linux
[ASP.NET Core 3框架揭秘] 跨平台开发体验: Docker