[ASP.NET Core 3框架揭秘] 跨平台開發體驗: Windows [中篇]


我們在《上篇》利用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組合鍵就可以了。

1-6

注冊的KestrelServer服務器會綁定到http//localhost:5000和https//localhost:5001這兩個地址監聽請求,如果我們利用瀏覽器分別對這兩個地址發起請求會得到怎樣的響應呢?如下圖所示,兩個請求都會得到主體內容為“Hello World.”的響應(由於證書的問題,Chrome瀏覽器為HTTPS的請求會顯示“Not secure”的警告),毫無疑問該內容就是我們注冊的中間件寫入的。

1-7

三、修改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)”選項卡下的所有設置最終都會體現在該文件上。

1-8

如果在launchSettings.json文件設置了多個Profile,它們會以如下圖所示的形式出現在Visual Studio的工具欄中,我們可以選擇任意一個Profile中定義的配置選項來啟動當前應用程序。如果Profile中通過設置launchBrowser屬性選擇啟動瀏覽器,我們還可以選擇瀏覽器的類型。

1-9

如果我們在當前項目所在目錄下通過執行dotnet run命令來啟動應用程序,launchSettings.json文件會默認被加載。我們可以通過命令行參數--launch-profile指定采用的Profile。如果沒有對Profile作顯式指定,定義在該配置文件中第一個commandName為“Project”的Profile會默認被使用。如下圖所示,我們在創建的應用根目錄下通過執行dotnet run命令啟動我們的應用程序,其中第一次執行dotnet run命令的時候顯式設置了Profile名稱(--launch-profile helloworld)。從輸出的結果可以看出,兩次采用的是同一個Profile。

1-10

如果在執行dotnet run命令的時候不希望加載launchSettings.json文件,我們可以通過顯式指定命令行參數--no-launch-profile來實現。如下圖所示,我們在執行dotnet run命令時指定了--no-launch-profile參數,所以應用會采用KestrelServer默認的監聽地址(http://localhost:5000和https://localhost:5001)。由於launchSettings.json根本就沒有被加載,所以當前執行環境從Development變成了默認的Production。

1-11

五、顯式指定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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM