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遠遠沒有那麼簡單,容我娓娓道來。html
老規矩,從Hello World 開始,咱們建立一個Demo項目。
記住開始以前請確認已安裝.NET Core 2.1 SDK!!!
咱們此次使用命令行來建立項目:git
>dotnet --version 2.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打開解決方案,以下圖:github
<h1>This is from CommonUI.Page1</h1>
咱們觀察到RPL.CommonUI中預置了一個Razor Page,由於Razor Page是基於文件系統路由,因此直接https://localhost:<port>/myfeature/page1
便可訪問。
web
到這一步,咱們就能夠篤定RPL正確生效。api
以上只是簡單的HTML頁面,若是要想加以潤色,就須要寫CSS來處理。
兩種處理方式:瀏覽器
內聯樣式,很簡單,就不加以贅述。
咱們來定義樣式文件來處理。仿照RPL.Web項目,建立一個wwwroot根目錄,而後再添加一個css文件夾,再添加一個demo.css的樣式文件。mvc
h1 { color: red; }
而後將demo.css引用添加到page1.cshtml中。app
<head> <meta name="viewport" content="width=device-width" /> <link rel="stylesheet" href="~/css/demo.css" /> <title>Page1</title> </head>
CTRL+F5從新運行,運行結果以下圖:
ide
能夠清晰的看到,定義的樣式並未生效。從瀏覽器F12 Developer Tool中能夠清晰的看到,沒法請求demo.css樣式文件。
到這裏,也就拋出了本文所要解決的問題:如何開發獨立通用的RPL?
若是RPL中沒法引用項目中定義一些靜態資源文件(CSS、JS、Image等),那RPL將沒法有效的組織View。
要想訪問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 = new PhysicalFileProvider(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時,還須要輸出靜態資源文件,顯然增長了使用的難度。而如何將資源文件打包進程序集呢?——內嵌資源。
一個程序集主要由兩種類型的文件構成,它們分別是承載IL代碼的託管模塊文件和編譯時內嵌的資源文件。那在.NET Core中如何定義內嵌資源呢?
<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的地方,添加這麼幾句代碼,由於這段代碼有很強的侵入性,且不可隔離變化。
<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
,定義以下:using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; using System; namespace RPL.CommonUI { internal class CommonUIConfigureOptions: IPostConfigureOptions<StaticFileOptions> { public CommonUIConfigureOptions(IHostingEnvironment environment) { Environment = environment; } public IHostingEnvironment Environment { get; } public void PostConfigure(string name, StaticFileOptions options) { name = name ?? throw new ArgumentNullException(nameof(name)); options = options ?? throw new ArgumentNullException(nameof(options)); // Basic initialization in case the options weren't initialized by any other component options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider(); if (options.FileProvider == null && Environment.WebRootFileProvider == null) { throw new InvalidOperationException("Missing FileProvider."); } options.FileProvider = options.FileProvider ?? Environment.WebRootFileProvider; // Add our provider var filesProvider = new ManifestEmbeddedFileProvider(GetType().Assembly, "wwwroot"); options.FileProvider = new CompositeFileProvider(options.FileProvider, filesProvider); } } }
CommonUIServiceCollectionExtensions.cs
,代碼以下:using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Text; namespace RPL.CommonUI { public static class CommonUIServiceCollectionExtensions { public static void AddCommonUI(this IServiceCollection services) { services.ConfigureOptions(typeof(CommonUIConfigureOptions)); } } }
修改RPL.Web啓動類startup.cs,在services.AddMvc()
以前添加services.AddCommonUI();
便可。
CTRL+F5從新運行,咱們發現H1被成功設置爲紅色,檢查發現demo.css也能正確被請求,檢查network也能夠看到其Request URL爲:https://localhost:44379/css/demo.css
Demonstrate how to use Razor class library to create reusable email template.
這個連接是一個進階demo,演示瞭如何使用RPL去建立可重用的郵件模板,感興趣的不妨一看。