Razor Page Library:開發獨立通用RPL(內嵌wwwroot資原始檔夾)
Demo路徑:https://github.com/yanshengjie/RPL.Demo
1. Introduction
Razor Page Library 是ASP.NET Core 2.1引入的新類庫專案,屬於新特性之一,用於建立通用頁面公用類庫。也就意味著可以將多個Web專案中通用的Web頁面提取出來,封裝成RPL,以進行程式碼重用。
官方文件Create reusable UI using the Razor Class Library project in ASP.NET Core中,僅簡單介紹瞭如何建立RPL,但要想開發出一個獨立通用的RPL遠遠沒有那麼簡單,容我娓娓道來。
2. Hello RPL
老規矩,從Hello World 開始,我們建立一個Demo專案。
記住開始之前請確認已安裝.NET Core 2.1 SDK!!!
我們這次使用命令列來建立專案:
>dotnet --version2.1.300>dotnet new razorclasslib --name RPL.CommonUI
已成功建立模板“Razor Class Library”。
正在處理建立後操作...正在 RPL.CommonUI\RPL.CommonUI.csproj 上執行 "dotnet restore"...
正在還原 F:\Coding\Demo\RPL.CommonUI\RPL.CommonUI.csproj 的包...
正在生成 MSBuild 檔案 F:\Coding\Demo\RPL.CommonUI\obj\RPL.CommonUI.csproj.nuge
t.g.props。
正在生成 MSBuild 檔案 F:\Coding\Demo\RPL.CommonUI\obj\RPL.CommonUI.csproj.nuge
t.g.targets。 F:\Coding\Demo\RPL.CommonUI\RPL.CommonUI.csproj 的還原在 1.34 sec 內完成。
還原成功。
>dotnet new mvc --name RPL.Web
已成功建立模板“ASP.NET Core Web App (Model-View-Controller)”。
此模板包含非 Microsoft 的各方的技術,有關詳細資訊,請參閱 https://aka.ms/aspnetc
ore-template-3pn-210 。
正在處理建立後操作...正在 RPL.Web\RPL.Web.csproj 上執行 "dotnet restore"...
正在還原 F:\Coding\Demo\RPL.Web\RPL.Web.csproj 的包...
正在生成 MSBuild 檔案 F:\Coding\Demo\RPL.Web\obj\RPL.Web.csproj.nuget.g.props
。
正在生成 MSBuild 檔案 F:\Coding\Demo\RPL.Web\obj\RPL.Web.csproj.nuget.g.target
s。 F:\Coding\Demo\RPL.Web\RPL.Web.csproj 的還原在 2 sec 內完成。
還原成功。
>dotnet new sln --name RPL.Demo
已成功建立模板“Solution File”。
>dotnet sln RPL.Demo.sln add RPL.CommonUI/RPL.CommonUI.csproj
已將專案“RPL.CommonUI\RPL.CommonUI.csproj”新增到解決方案中。
>dotnet sln RPL.Demo.sln add RPL.Web/RPL.Web.csproj
已將專案“RPL.Web\RPL.Web.csproj”新增到解決方案中。
建立完畢後,雙擊RPL.Demo.sln開啟解決方案,如下圖:
修改Page1.cshtml,body內新增
<h1>This is from CommonUI.Page1</h1>
RPL.Web新增引用專案【RPL.CommonUI】
設定RPL為啟動專案。
CTRL+F5執行。
我們觀察到RPL.CommonUI中預置了一個Razor Page,因為Razor Page是基於檔案系統路由,所以直接https://localhost:<port>/myfeature/page1
即可訪問。
到這一步,我們就可以篤定RPL正確生效。
3. Keep Going
以上只是簡單的HTML頁面,如果要想加以潤色,就需要寫CSS來處理。
兩種處理方式:
使用內聯樣式
引用外部樣式檔案
內聯樣式,很簡單,就不加以贅述。
我們來定義樣式檔案來處理。仿照RPL.Web專案,建立一個wwwroot根目錄,然後再新增一個css資料夾,再新增一個demo.css的樣式檔案。
h1 { color: red;
}
然後將demo.css引用新增到page1.cshtml中。
<head>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="~/css/demo.css" />
<title>Page1</title></head>
CTRL+F5重新執行,執行結果如下圖:
可以清晰的看到,定義的樣式並未生效。從瀏覽器F12 Developer Tool中可以清晰的看到,無法請求demo.css樣式檔案。
到這裡,也就丟擲了本文所要解決的問題:如何開發獨立通用的RPL?
如果RPL中無法引用專案中定義一些靜態資原始檔(CSS、JS、Image等),那RPL將無法有效的組織View。
4. Analyze
要想訪問RPL中的靜態資原始檔,首先我們要弄明白.NET Core Web專案中wwwroot資料夾的資源是如何訪問的。
這一切得從應用程式啟動說起,為了方便查閱,使用Code Map將相關程式碼顯示如下:
從中可以看出在構建WebHost的業務邏輯中會去初始化IHostingEnvironment
物件。該物件主要用來描述應用程式執行的web宿主環境的相關資訊,主要包含以下幾個屬性:
string EnvironmentName { get; set; }string ApplicationName { get; set; }string WebRootPath { get; set; }
IFileProvider WebRootFileProvider { get; set; }string ContentRootPath { get; set; }
IFileProvider ContentRootFileProvider { get; set; }
從上圖的註釋程式碼中可以看到,其初始化邏輯正是去指定WebRootPath
和WebRootFileProvider
。
如果我們在應用程式未手動通過webHostBuilder.UseWebRoot("your web root path");
指定自定義的Web Root路徑,那麼將會預設指定為wwwroot
資料夾。
同時注意下面這段程式碼:
hostingEnvironment.WebRootFileProvider = newPhysicalFileProvider(hostingEnvironment.WebRootPath);
其指定的IFileProvider
的型別為PhysicalFileProvider
。
到這裡,是不是就豁然開朗了,Web 應用啟動時,指定的WebRootFileProvider
僅僅映射了Web應用的wwwroot目錄,自然是訪問不了我們RPL專案指定的wwwroot目錄啊。
到這裡,其實我們離問題就很近了。但是隻要指定了WebRootFileProvider
就可以訪問WebRoot目錄的資源了嗎?並不是。
我們知道,ASP.NET Core是通過由一系列中介軟體組裝而成的請求管道來處理請求的。不管是View檢視也好,還是靜態資原始檔也好,都是通過Http Request來請求的。HTTP Request流入請求管道後,根據請求型別,不同的中介軟體負責處理不同的請求。那對於靜態資原始檔,ASP.NET Core中是藉助StaticFileMiddleware
中介軟體來處理的。這也就是為什麼在啟動類Startup
的Configure
方法中需要指定app.UseStaticFiles();
來啟用StaticFileMiddleware
中介軟體。
在ASP.NET Core 官方文件中Static files in ASP.NET Core,介紹瞭如何訪問自定義目錄的靜態資原始檔。
如果需要訪問自定義路徑目錄的資源,需要新增類似以下程式碼:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
但這似乎並不能滿足我們的需求。Why?看標題,開發獨立通用的RPL。怎麼理解獨立通用?也就意味著RPL中的資原始檔最好能夠通過程式集打包。這樣才能完全獨立。否則,在釋出RPL時,還需要輸出靜態資原始檔,顯然增加了使用的難度。而如何將資原始檔打包程序序集呢?——內嵌資源。
5. Embedded Resource
一個程式集主要由兩種型別的檔案構成,它們分別是承載IL程式碼的託管模組檔案和編譯時內嵌的資原始檔。那在.NET Core中如何定義內嵌資源呢?
編輯RPL.CommonUI.csproj檔案,新增wwwroot為內嵌資源。
<ItemGroup><EmbeddedResource Include="wwwroot\**\*" /> </ItemGroup>
新增
GenerateEmbeddedFilesManifest
節點,指定生成內嵌資源清單。<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
新增
Microsoft.Extensions.FileProviders.Embedded
Nuget包引用。
修改完後的RPL.CommonUI.csproj,如下所示:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\**\*" />
</ItemGroup></Project>
我們用ildasm.exe反編譯RPL.CommonUI.dll,檢視下其程式集清單:
從圖中可以看出內嵌的demo.css檔案,是以{程式集名稱}.{檔案路徑}命名的。
那內嵌資源如何訪問呢?可以藉助EmbeddedFileProvider
,我們仿照上面的例子,在Startup.cs
的Configure
方法中新增以下程式碼:
app.UseStaticFiles();var dllPath = Path.Join(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "RPL.CommonUI.dll");
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new ManifestEmbeddedFileProvider(Assembly.LoadFrom(dllPath), "wwwroot")
});
CTRL+F5,執行。Perfect!
當然這也不是最好的解決方案,因為你肯定不想所有呼叫這個RPL的地方,新增這麼幾句程式碼,因為這段程式碼有很強的侵入性,且不可隔離變化。
5. Final Solution
編輯RPL.CommonUI.csproj檔案,新增wwwroot為內嵌資源。
<ItemGroup><EmbeddedResource Include="wwwroot\**\*" /> </ItemGroup>
新增
GenerateEmbeddedFilesManifest
節點,指定生成內嵌資源清單。<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
新增
Microsoft.AspNetCore.StaticFiles
和Microsoft.Extensions.FileProviders.Embedded
Nuget包引用。
修改完後的RPL.CommonUI.csproj,如下所示:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\**\*" />
</ItemGroup></Project>
接下來新增
CommonUIConfigureOptions.cs
,定義如下:
然後新增
CommonUIServiceCollectionExtensions.cs
,程式碼如下:
修改RPL.Web啟動類startup.cs,在
services.AddMvc()
之前新增services.AddCommonUI();
即可。CTRL+F5重新執行,我們發現H1被成功設定為紅色,檢查發現demo.css也能正確被請求,檢查network也可以看到其Request URL為:https://localhost:44379/css/demo.css
Static files in ASP.NET Core
File Providers in ASP.NET Core
ManifestEmbeddedFileProvider Class
Make it easier to use static assets that are part of a RCL project
.NET Core的檔案系統[4]:由EmbeddedFileProvider構建的內嵌(資源)檔案系統
相關文章:
原文地址: https://www.cnblogs.com/sheng-jie/p/9165547.html
.NET社群新聞,深度好文,歡迎訪問公眾號文章彙總 http://www.csharpkit.com