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打开解决方案,如下图:

  1. 修改Page1.cshtml,body内添加<h1>This is from CommonUI.Page1</h1>

  2. RPL.Web添加引用项目【RPL.CommonUI】

  3. 设置RPL为启动项目。

  4. CTRL+F5运行。

我们观察到RPL.CommonUI中预置了一个Razor Page,因为Razor Page是基于文件系统路由,所以直接https://localhost:<port>/myfeature/page1即可访问。

到这一步,我们就可以笃定RPL正确生效。

3. Keep Going

以上只是简单的HTML页面,如果要想加以润色,就需要写CSS来处理。
两种处理方式:

  1. 使用内联样式

  2. 引用外部样式文件

内联样式,很简单,就不加以赘述。
我们来定义样式文件来处理。仿照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; }

从上图的注释代码中可以看到,其初始化逻辑正是去指定WebRootPathWebRootFileProvider
如果我们在应用程序未手动通过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中间件来处理的。这也就是为什么在启动类StartupConfigure方法中需要指定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中如何定义内嵌资源呢?

  1. 编辑RPL.CommonUI.csproj文件,添加wwwroot为内嵌资源。

    <ItemGroup><EmbeddedResource Include="wwwroot\**\*" /></ItemGroup>
  2. 添加GenerateEmbeddedFilesManifest节点,指定生成内嵌资源清单。

    <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
  3. 添加Microsoft.Extensions.FileProviders.EmbeddedNuget包引用。

修改完后的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.csConfigure方法中添加以下代码:

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.StaticFilesMicrosoft.Extensions.FileProviders.EmbeddedNuget包引用。

修改完后的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

  1. Static files in ASP.NET Core

  2. File Providers in ASP.NET Core

  3. ManifestEmbeddedFileProvider Class

  4. Make it easier to use static assets that are part of a RCL project

  5. .NET Core的文件系统[4]:由EmbeddedFileProvider构建的内嵌(资源)文件系统

相关文章: 

  • 用ASP.NET Core 2.1 建立规范的 REST API -- HATEOAS

  • 在ASP.NET Core中使用brotli压缩

  • ASP.NET Core2.1 你不得不了解的GDPR

  • .NET Core 2.1改进了性能,并提供了新的部署选项

  • Entity Framework Core 2.1带来更好的SQL语句生成方案

原文地址: https://www.cnblogs.com/sheng-jie/p/9165547.html



.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

Razor Page Library:开发独立通用RPL(内嵌wwwroot资源文件夹)相关推荐

  1. python程序实现:linux环境下,将一个文件夹内的所有.tar.gz压缩文件解压缩到同名子文件夹内,没有子文件夹时重新建立同名子文件夹,使得它们相互独立。

    # 命名为untar.pyimport tarfile import os# 获得目录下的所有文件 def get_file_name(file_dir):L = []for root, dirs, ...

  2. stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载第五章 SYSTEM 文件夹

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第五章 SYSTEM 文 ...

  3. Android高德地图基本开发/在线高德离线地图开发/断网使用离线地图(Assets文件夹的使用)

    文章目录 高德SDK基本使用 前置操作 需求一:显示地图,并以当前所在位置为中心 权限申请 布局功能代码 活动功能代码 效果展示 需求二:离线地图(直接添加到应用端项目内) 需求三 :点击数据后以数据 ...

  4. 将图片内嵌到 exe 文件中

    场景描述: 如果程序中需要使用一张图片,通常会用相对路径方式指定一个外部文件.例如: loadimage(NULL, "test.jpg"); 这样,将编译后的 .exe 和 te ...

  5. 开发一坑整理:IDEA无法打开文件夹,打开就卡死,完全卸载IDEA

    目录 问题现象 解决过程 最终解决方案 分享一个自己刚刚遇到的坑,是关于我们开发工具,IDEA 问题现象 用的好好的,突然 IDEA 闪退,再打开的时候,以前的设置都没了,包括背景设置,快捷键设置,再 ...

  6. 【Android游戏开发二十七】讲解游戏开发与项目下的hdpi 、mdpi与ldpi资源文件夹以及游戏高清版本的设置...

    今天一个开发者问到我为什么游戏开发要删除项目下的hdpi.mdpi和ldpi文件夹:下面详细给大家解答一下: 首先童鞋们如果看过我写的<[Android游戏开发二十一]Android os设备谎 ...

  7. 基于Visua C++2010 与 Windows 7 SDK开发windows7 Shell应用(1)-搜索文件夹

    windows7 shell 就是windows7操作系统的的控制台 对于熟悉了命令行的人而言,用键盘调用程序比鼠标更快更省力 您可以用"Shell:"命令调用一切可以用资源管理器 ...

  8. android go rom下载地址,一步一步学Android ROM开发(一)——修改现有ROM资源文件-Go语言中文社区...

    一,准备工作 1.一台开发手机. 我推荐最好用G7,也是就HTC Desire那款(是不是有些广告嫌疑...),这台机各种ROM都很全,资料也多些,很容易刷到root,android版本也是最新的2. ...

  9. 一步一步学Android ROM开发(一)——修改现有ROM资源文件

    一,准备工作 1.一台开发手机. 我推荐最好用G7,也是就HTC Desire那款(是不是有些广告嫌疑...),这台机各种ROM都很全,资料也多些,很容易刷到root,android版本也是最新的2. ...

最新文章

  1. 精美图文讲解Java AQS 共享式获取同步状态以及Semaphore的应用
  2. Github上Pandas,Numpy和 Scipy三个库中20个最常用的函数
  3. Windows应用程序类型和相应的入口点函数
  4. python中if brthon环境安装包_Ant、Gradle、Python三种打包方式的介绍
  5. 初识Mysql(part17)--我需要知道的3条Mysql语句之联结
  6. ES6 Map和Set的用法笔记
  7. 自动驾驶路径规划论文解析(2)
  8. eclipse中 将java项目转换为web项目
  9. 揭露动画(Reveal Effect)实现时的注意事项(附上bug-logcat)...
  10. centos mysql无法登录,解决centos下MySQL登录1045问题
  11. SQL Server中的部分存储过程
  12. mybatis---知识点复习
  13. 【纯干货】4年前想解决的事情,今天才实验成功
  14. 大麦网抢票程序(一)之大麦网网站分析
  15. paypal php 退款,PayPal的Restful-API方式退款、WEB支付、回调
  16. 微信小程序消息通知-打卡考勤
  17. 千万别用MongoDB?
  18. python ttk separator_python - ttk.Separator设置长度/宽度 - 堆栈内存溢出
  19. 《网络安全》课程教案
  20. 怎样隐藏计算机中的文件夹,电脑上的文件夹不想被别人看到怎么办?如何隐藏电脑文件夹?-电脑文件夹怎么加密...

热门文章

  1. 朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上)
  2. Druid源码分析系列1:dataSource.init()的准备工作
  3. 图片加载框架Picasso - 源码分析
  4. eclipse各种配置
  5. linux系统将python升级到2.7.10版本
  6. Spring与SpringMVC集成出现的问题
  7. 全国计算机技术与软件专业技术资格(水平)考试基础知识
  8. K8s 中使用 cert-manager 申请免费 Https 证书
  9. 设计模式:面向对象的设计原则下(ISP、DIP、KISS、YAGNI、DRY、LOD)
  10. VisualStudio配置中文提示