簡體   English   中英

ASP.NET Core 2.1 中的 UseStaticFiles、UseSpaStaticFiles 和 UseSpa 有什么區別?

[英]What is the difference between UseStaticFiles, UseSpaStaticFiles, and UseSpa in ASP.NET Core 2.1?

ASP.NET Core 2.1.1 為 appBuilder 提供了幾種看似相關的擴展方法:

  • 來自Microsoft.AspNetCore.StaticFilesUseStaticFiles
  • 來自Microsoft.AspNetCore.SpaServices.ExtensionsUseSpaStaticFiles
  • 來自Microsoft.AspNetCore.SpaServices.ExtensionsUseSpa

請幫助我理解他們的目的和彼此之間的關系?

另外,如果我以不同的順序運行這些方法,從服務器執行的角度來看有什么不同嗎

例如

app.UseStaticFiles() -> app.UseSpaStaticFiles() -> app.UseSpa()

對比

app.UseSpa() -> app.UseSpaStaticFiles() -> app.UseStaticFiles()

靜態文件(例如 HTML、CSS、圖像和 JavaScript)是 ASP.NET Core 應用程序直接提供給客戶端的資產。 需要一些配置才能啟用這些文件的服務。

  • UseStaticFiles - 在 Web 根目錄(wwwroot 文件夾)內提供文件

  • UseSpaStaticFiles - 在 angular 應用程序的資產文件夾中提供靜態文件,如圖像、css、js

  • UseSpa - 讓 asp.net core 知道你想要運行你的 angular 應用程序的目錄,在生產模式下運行時的 dist 文件夾以及在開發模式下運行 angular 應用程序的命令

例子

services.AddSpaStaticFiles(configuration =>
{
 configuration.RootPath = "ClientApp/dist";
});

app.UseSpa(spa =>
{
    // To learn more about options for serving an Angular SPA from ASP.NET Core,
    // see https://go.microsoft.com/fwlink/?linkid=864501

    spa.Options.SourcePath = "ClientApp";

    if (env.IsDevelopment())
    {
        spa.UseAngularCliServer(npmScript: "start");
    }
});
  1. UseStaticFiles提供來自 wwwroot 的文件,但它可以更改。
  2. UseSpaStaticFiles做類似的事情,但它需要注冊 ISpaStaticFileProvider。 如果app.ApplicationServices.GetService<ISpaStaticFileProvider>()返回 null,則您將收到異常。
throw new InvalidOperationException($"To use {nameof(UseSpaStaticFiles)}, you must " +
                    $"first register an {nameof(ISpaStaticFileProvider)} in the service provider, typically " +
                    $"by calling services.{nameof(AddSpaStaticFiles)}.");

所以你需要調用app.AddSpaStaticFiles()來注冊默認的ISpaStaticFileProvider

  1. UseSpa做兩件事。 將所有請求重寫到默認頁面並嘗試配置靜態文件服務。 UseSpaStaticFiles相反,它不會拋出異常,而只是回退到 wwwroot 文件夾。

實際上 UseSpaStaticFiles 和 UseSpa 都在內部調用相同的方法 UseSpaStaticFilesInternal 但第三個參數的值不同,即allowFallbackOnServingWebRootFiles 這就是為什么 UseSpaStaticFiles 在沒有注冊 ISpaStaticFileProvider 時拋出異常的原因,它根本不允許回退到 wwwroot。

順便說一句,如果UseSpa在內部回退到 wwwroot,它會調用舊的好app.UseStaticFiles(staticFileOptions);

github 源鏈接:1. SpaDefaultMiddleware 2. SpaStaticFilesExtensions

我最近深入研究了它是如何工作的,我想我會在這里詳細解釋一下,因為我認為它沒有那么好記錄。 我已經研究了它如何與 Visual Studio 的 React 模板一起工作,但它也適用於其他 SPA 框架。

要理解的重要一點是,也許與更傳統的 ASP.NET web 應用程序不同: ASP.NET SPA 站點在開發中的運行方式與在生產中的運行方式完全不同。

記住以上內容非常重要,因為這些東西的大部分內部實現只適用於生產,當您不將請求路由到 Webpack 開發網絡服務器時。 大多數相關的 ASP.NET SPA 代碼都位於源代碼庫中的這個位置,我將在下面偶爾引用它。 如果您想真正深入研究實現,請看那里。

SPA 在 ASP.NET 中的工作原理

因此,按照模板中默認調用的順序,通過各種 SPA 調用到 go:

// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration => {
    configuration.RootPath = "ClientApp/build";
});

正如我將在下面解釋的那樣,該評論有點誤導。 這在ConfigureServices中調用, 並將DefaultSpaStaticFileProvider添加為 DI 的ISpaStaticFileProvider 如果您稍后要調用UseSpaStaticFiles (您可能是這樣),這是必需的。 RootPath在此處指定(請參閱稍后了解它的作用)。 接下來(其余調用在Configure中進行):

app.UseStaticFiles();

這是很好的舊UseStaticFiles ,從wwwroot目錄提供 static 文件的久經考驗的方式。 這是在其他人之前調用的,這意味着如果請求的路徑存在於wwwroot中,它將立即從那里提供服務,而不是在 SPA 目錄中查找(在 development 期間默認為ClientApp/public ,或默認為ClientApp/build在生產過程中- 請參閱下面的“static 資產從哪里獲得服務?”)。 如果那里不存在,它將落入下一個中間件,即:

app.UseSpaStaticFiles();

這類似於app.UseStaticFiles ,但從wwwroot的不同目錄提供 static 文件 - 您的“SPA”目錄,默認為ClientApp 然而,雖然它在技術上可以在開發期間工作,但它只是打算在生產期間做任何事情。 它在上面提到的RootPath目錄中查找 - 類似於ClientApp/build - 並嘗試從那里提供文件(如果存在)。 此目錄將存在於已發布的生產站點中,並將包含從ClientApp/public復制的 SPA 內容,以及由 Webpack 生成的內容。 但是,即使在站點運行時仍注冊UseSpaStaticFiles ,它也可能會失敗,因為ClientApp/build在 development 期間不存在 為什么不? 如果您發布您的應用程序, ClientApp/build目錄確實會創建在您項目的根目錄下。 但是 SPA 模板依賴於它在開發過程中被刪除,因為當您稍后運行app.UseSpa時,它最終會運行npm run start ,其中(如果您查看package.json將運行類似:

"start": "rimraf ./build && react-scripts start",

注意build目錄的破壞。 UseSpaStaticFiles依賴於npm腳本,該腳本由稍后的中間件觸發,以刪除build目錄並有效地阻止它在開發過程中劫持管道! 如果您在啟動站點后手動恢復該build目錄,即使在開發過程中,此中間件也會從中提供文件。 而是希斯·羅賓遜。 正如我上面提到的,關於從這個目錄“生產”中提供 React 文件的評論有點誤導,因為在開發期間它們也會從這里提供。 只是假設這個目錄在開發過程中不會存在。 為什么他們不只是在模板中放這樣的東西我不確定:

if (!env.IsDevelopment()) {
    app.UseSpaStaticFiles();
}

實際上,我建議您將if子句放入,這樣您就不會依賴從文件系統中刪除build 但是不管怎樣,這個固件是打算在開發過程中100%通過的,因為在向ASP.NET發出請求時應該刪除該目錄,到最終的中間件:

app.UseSpa(spa => {
    //spa.Options.SourcePath = "ClientApp";
    // ^ as this is only used in development, it's misleading to put it here.  I've moved
    // it inside the following 'if' clause.

    if (env.IsDevelopment()) {
        spa.Options.SourcePath = "ClientApp";

        // This is called for the React SPA template, but beneath the surface it, like all
        // similar development server proxies, calls
        // 'SpaProxyingExtensions.UseProxyToSpaDevelopmentServer', which causes all
        // requests to be routed through to a Webpack development server for React, once
        // it's configured and started that server.
        spa.UseReactDevelopmentServer(npmScript: "start");
    }
});

這個“包羅萬象”的中間件將所有請求重寫到您正在使用的 SPA 框架的“默認頁面”,該頁面由spa.Options.DefaultPage確定, 默認為/index.html 因此,默認情況下,所有請求都會在生產期間呈現/index.html ,從而允許客戶端應用程序始終加載其頁面並適當地處理對不同 URL 的請求。

但是,由於上述if (env.IsDevelopment()) {... }子句中的內容, 此中間件不打算在 development 期間受到影響。 UseReactDevelopmentServer (或最終調用UseProxyToSpaDevelopmentServer ,或者直接調用UseProxyToSpaDevelopmentServer )添加了一個終端中間件,它將所有請求代理到 Webpack 開發服務器,實際上阻止了請求通過UseSpa中間件。 因此,在生產過程中,該中間件運行並充當“包羅萬象”來呈現index.html 但在開發過程中,它根本打算運行,請求應該首先被代理中間件攔截,該中間件轉發到 Webpack 開發服務器並返回其響應。 Webpack 開發服務器是在spa.Options.SourcePath指定的工作目錄中啟動的,因此它將在開發過程中將ClientApp/public/index.html作為包羅萬象的網頁提供spa.Options.SourcePath/public/ ??? 見下文。) spa.Options.SourcePath選項在生產期間不使用。

static 資產從哪里獲得服務?

接受對文件/logo.png的給定請求。 wwwroot將首先被檢查,如果它存在,它將被提供。 production期間,將檢查ClientApp/build的文件,因為這是在services.AddSpaStaticFiles中配置的路徑。 然而,在開發過程中,該路徑實際上沒有被檢查(它是,但它應該在開發開始之前一直被刪除;見上文),並且將檢查 static 資產的路徑是項目根目錄中的ClientApp/public . 這是因為請求將被代理到內部 Webpack 開發服務器。 Webpack 開發服務器既提供動態生成的 Webpack 資產,也提供 static 資產,例如/logo.png ,從其“靜態目錄”選項, 默認為public目錄。 由於服務器在ClientApp工作目錄中啟動(感謝spa.Options.SourcePath選項),它將嘗試從ClientApp/public提供 static 資產。

總結——執行流程

Basically, ASP.NET's SPA methods are trying to roughly emulate at production what the Webpack development server does at development - serve static assets first (although ASP.NET also tries wwwroot first, which the Webpack dev server obviously doesn't do), then fall through到默認index.html用於 SPA 應用程序(Webpack 開發服務器默認情況下不執行此操作,但諸如react-scripts類的設置有一個默認設置,它添加了一個導致這種情況發生的插件)。

但是,在開發時,ASP.NET 實際上通過該 Webpack 開發服務器執行代理請求,因此其 SPA 中間件基本上是為了繞過。 以下是兩種情況下預期流程的摘要:

生產

Request
-> Check in /wwwroot
-> Check in /ClientApp/build
-> Rewrite request path to /index.html and serve that from /ClientApp/build

發展

Request
-> Check in /wwwroot
-> Proxy through to internal Webpack dev web server, which:
-> ... serves static assets from /ClientApp/public
-> ... serves dynamically-generated Webpack assets from their locations
-> ... failing that, rewrites request path to /index.html and serves that from /ClientApp/public

一個額外的WTF

微軟做出了一些設計決定,我認為他們如何做到這一點有點值得懷疑,但有一種行為對我來說毫無意義,我不知道它的用例,但值得一提。

app.UseSpa最終調用app.UseSpaStaticFilesInternal並將allowFallbackOnServingWebRootFiles選項設置為true 唯一有效的情況是,如果您之前沒有ISpaStaticFileProvider添加到 DI(例如,通過調用services.AddSpaStaticFiles ),並且仍然調用app.UseSpa 在這種情況下,它不會拋出異常,而是從wwwroot提供文件。 老實說,我不知道這是什么意思。 模板已經先調用app.UseStaticFiles ,所以來自wwwroot的文件已經被優先處理。 如果您刪除它,刪除services.AddSpaStaticFiles並且不調用app.UseSpaStaticFiles (因為這會引發異常),那么app.UseSpa將從wwwroot提供文件。 如果那有用例,我不知道它是什么。

進一步的想法

此設置工作正常,但 Webpack 開發服務器的“按需”設置似乎啟動了大量節點實例,這對我來說似乎相當低效。 正如本博客中“更新的開發設置”下所建議的那樣(它還提供了一些關於這個 SPA 東西如何工作的很好的見解),在開始時手動運行 Webpack 開發服務器(或 Angular/React/whatever)可能是一個更好的主意您的開發 session,並將開發服務器的按需創建( spa.UseReactDevelopmentServer(npmScript: "start") )更改為對現有開發服務器的按需創建代理( spa.UseProxyToSpaDevelopmentServer("http://localhost:4200") ),這應該避免啟動 101 個節點實例。 這也避免了每次 ASP.NET 源代碼更改時重建 JS 的一些不必要的緩慢。

嗯... MS 付出了很大的努力來允許代理通過節點支持的 Webpack 開發 web 服務器,如果他們只是建議在生產中部署基於節點的 Z2567A5EC43705EB189DZE8 服務器幾乎會更簡單請求通過 to。 這將避免在生產和開發中行為不同的所有額外代碼。 哦,好吧,我想這樣,至少當你開始生產時,你不依賴節點。 但是您幾乎處於開發階段。 考慮到所有 SPA 生態系統是如何設計為與節點一起工作的,這可能是不可避免的。 在 SPA 應用程序中,node 已有效地成為必要的構建工具,類似於gcc之類的工具。 您不需要它來運行已編譯的應用程序,但您肯定需要它來從源代碼轉換它。 我猜想手工制作 JavaScript 以在瀏覽器中運行,在這個類比中,就像手寫程序集一樣。 技術上可行,但沒有真正做到。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM