![](/img/trans.png)
[英]UseSpaStaticFiles() in asp.net core 3 looking inside embedded resources
[英]What is the difference between UseStaticFiles, UseSpaStaticFiles, and UseSpa in ASP.NET Core 2.1?
ASP.NET Core 2.1.1 為 appBuilder 提供了幾種看似相關的擴展方法:
Microsoft.AspNetCore.StaticFiles
的UseStaticFiles
Microsoft.AspNetCore.SpaServices.Extensions
的UseSpaStaticFiles
Microsoft.AspNetCore.SpaServices.Extensions
的UseSpa
請幫助我理解他們的目的和彼此之間的關系?
另外,如果我以不同的順序運行這些方法,從服務器執行的角度來看有什么不同嗎
例如
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");
}
});
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
實際上 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 調用到 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
選項在生產期間不使用。
接受對文件/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
微軟做出了一些設計決定,我認為他們如何做到這一點有點值得懷疑,但有一種行為對我來說毫無意義,我不知道它的用例,但值得一提。
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.