【ASP.NET Core】标记帮助器——抽象层
标记帮助器,即 Tag Helpers。这个嘛,就直接翻译了,叫“标记帮助器”,虽然不好听,但只能这样了。当然你翻译为“标记增强器”也行。
所谓标记帮助器,就是针对 HTML 标签(不管是标准的还是自己命名的)进行扩展的做法。它是以 Razor 为基础的,服务于开发人员的。在服务器端用 C# 代码来实现一些需求,并生成 HTML 元素。在 Razor 文档中可以方便书写,VS 、VS Code 等工具还有提示功能。
不太恰当的理解就是把某个 HTML 标记封装为了一种组件,或者补充它原有的功能。不过,理解为一种组件也不算错,只不过不像 Razor 组件那样完整化的封装(里面是一大段HTML),Tag Helper 就是针对某个 HTML 元素的。
老周这篇水文不介绍常用的标记帮助器,毕竟这些大伙们都会用,就是在 Razor 文档中用 @addTagHeler 指令导入的那些类型。如内置的 input、form 元素的帮助器。像咱们常用的像 asp-controller 、asp-action 这些HTML属性就是通过帮助器来扩充的。
老周的想法是:咱们扒一下标记帮助器的底层知识,看能不能发现点啥乐子。生活不易,人世悲苦,“长太息以掩涕兮,哀民生之多艰”,所以得找点乐子充实一下人生。
咱们先聊最抽象的接口:ITagHelperComponent。咦?这货还真是以“Component”结尾,看来确实把标记帮助器认定为一种小型 Razor 组件。看看这接口为我们规范了些啥。
Order 属性:愚蠢的机器把它翻译为【订单】。这个错误很离谱,后果很严重。你要真按订单去理解,那就完了。这个是叫【顺序】,说直接点叫优先级。数值越小就越先被执行,比如,0、3、5,那么,Order 为0的先执行,Order为5的后执行。
Init 方法:看名字就知道这是初始化时被调用的。一般没有特别需要,这方法里不用写什么代码。方法有个 TagHelperContext 类型的参数。唯一能让你修改的是 Items 属性,它是个字典结构,用来存一些自定义数据。这些自定义数据可以在不同的 TagHelper 间传递。有点像 HttpContext.Items。
ProcessAsync 方法:这个是核核核心心心,重要的事延长三拍。各种为 HTML 元素添加属性、生成内容等都在此方法中完成。
实现 ITagHelperComponent 接口的类,在 Razor 文档中是不能被 @addTagHelper 指令导入的。咱们来做来试验。
[HtmlTargetElement("p")]
public class PragTagHelper : ITagHelperComponent
{
public int Order => 2; //这个优先级可以随意 public void Init(TagHelperContext context)
{
// 不用写代码
} public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// 内容之前
output.PreContent.SetHtmlContent("<strong>");
// 内容之后
output.PostContent.SetHtmlContent("</strong>");
return Task.CompletedTask;
}
}
@page
@addTagHelper TestApp.PragTagHelper, TestApp <p>孔明用枪打死了王司徒</p> <p>孔明用手雷轰死了王朗</p>
运行程序后,发现不起作用。生成的 HTML 文档没有插入<strong>元素。
然后,我把标记帮助器的代码改一改。这次咱们不实现 ITagHelperComponent 接口,而是 ITagHelper 接口。
[HtmlTargetElement("p")]
public class PragTagHelper : ITagHelper
{
……
}
然后再次运行。哟西,这下起作用了。
嗯,看来 ITagHelper 接口里面有文章,从声明可以看到,这个接口是继承 ITagHelperComponent 接口的。但这个接口是空的,没定义新成员。
public interface ITagHelper : ITagHelperComponent
{
}
这样就可以得出结论:ITagHelper 接口是一个标记接口,用来筛选出哪些类型可以用 @addTagHelper 指令引入——即哪些类型被认为是标记帮助器。
为了方便开发者定义自己的标记帮助器,ASP.NET Core 还提供了一个抽象类 TagHelper。
public abstract class TagHelper : ITagHelper, ITagHelperComponent
{
// 构造函数
protected TagHelper(); public virtual int Order { get; } public virtual void Init(TagHelperContext context); public virtual void Process(TagHelperContext context, TagHelperOutput output); public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
}
这个抽象类将接口的实现成员都声明为虚方法,派生时开发者可以按需重写。于是,咱们前面那个例子可以做以下修改:
[HtmlTargetElement("p")]
public class PragTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// 内容之前
output.PreContent.SetHtmlContent("<strong>");
// 内容之后
output.PostContent.SetHtmlContent("</strong>");
}
}
咱们前面试过,ITagHelperComponent 的实现类是不能被 @addTagHelper 指令发现的,那么,这个接口还有没有用呢?当然有用,只是直接实现这个接口的类,只针对<head>和<body>元素,通常用于大面积修改 HTML 的情形。比如,你要在 <body> 元素中插入一段 js 脚本,插入一堆HTML元素,插入一段CSS样式等。
来,咱们用例子来说明。
public class InsertStylesTagHelper : ITagHelperComponent
{
public int Order => 105; public void Init(TagHelperContext context)
{
// 空白
} public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// 要判断一下是不是<head>元素
if(output.TagName.Equals("head", StringComparison.OrdinalIgnoreCase))
{
// 插入以下CSS
string css = """
<style>
h5 {
color: blue;
} h3 {
color: green;
font-style: italic;
} p[setfont] {
font-family: '楷体';
}
</style>
"""; output.PreContent.AppendHtml(css);
}
return Task.CompletedTask;
}
}
这个帮助器就是在 <head> 元素内容的前面插入一段 supper style,不,是 CSS。因为CSS是一大段文本,这里老周用到了 C# 的原义文本块(就是不转义特殊字符),这个功能和 Python 中的差不多。只是C#要求左"""后要换行,右"""前也要换行。这样规定可能是为了写起代码来好看。
这种不实现 ITagHelper 的类型不能用 @addTagHelper 指令来引入,而是要添加到 ITagHelperComponentManager 接口的 Components 属性中,此属性是个列表对象,可以Add。
@page
@using Microsoft.AspNetCore.Mvc.Razor.TagHelpers
@using TestApp
@inject ITagHelperComponentManager tagHelperManager @{
// 手动添加TagHelper组件
var mytaghelper = new InsertStylesTagHelper();
// 添加到组件列表中
tagHelperManager.Components.Add(mytaghelper);
} <html>
<head>
<title>好看的例子</title>
<meta charset="UTF-8" />
</head>
<body>
<h3>三号标题</h3>
<h5>五号标题</h5>
<h2>二号标题 - 此处不应用样式</h2>
<p>其他内容 - 不应用样式</p>
<p setfont>使用楷书字体</p>
</body>
</html>
这样一弄,在运行程序后,自定义的 InsertStylesTagHelper 类就会自动应用到 head 标记上。
还没完呢,接下来咱们偷窥一下 TagHelperComponentManager 类的源代码。
internal sealed class TagHelperComponentManager : ITagHelperComponentManager
{
/// <summary>
/// Creates a new <see cref="TagHelperComponentManager"/>.
/// </summary>
/// <param name="tagHelperComponents">The collection of <see cref="ITagHelperComponent"/>s.</param>
public TagHelperComponentManager(IEnumerable<ITagHelperComponent> tagHelperComponents)
{
if (tagHelperComponents == null)
{
throw new ArgumentNullException(nameof(tagHelperComponents));
} Components = new List<ITagHelperComponent>(tagHelperComponents);
} /// <inheritdoc />
public ICollection<ITagHelperComponent> Components { get; }
}
其实这代码没啥好看的,只要注意它的构造函数就行了。不知道你看到这个构造函数想到了啥,老周想到了依赖注入。什么意思?就是说:你把实现 ITagHelperComponent 接口的类都注册为服务,那么,它就会自动起作用了,而且是面向整个应用程序的 Razor 代码。刚才咱们用依赖注入获取 ITagHelperComponentManager,并手动添加标记帮助器对象的方法是局部的,只对当前 Razor 文档有效。
所以,下面咱们把自己写的 InsertStylesTagHelper 注册为服务。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddTransient<ITagHelperComponent, InsertStylesTagHelper>();
var app = builder.Build();
最后回到 Razor 文档,打扫一下,手动添加到 Components 列表的代码现在不需要了。
@page <html>
<head>
<title>好看的例子</title>
<meta charset="UTF-8" />
</head>
<body>
<h3>三号标题</h3>
<h5>五号标题</h5>
<h2>二号标题 - 此处不应用样式</h2>
<p>其他内容 - 不应用样式</p>
<p setfont>使用楷书字体</p>
</body>
</html>
再次运行一下,你会发现也是可行的,<head>元素内也插入了 CSS 样式。
好了,今天咱们就聊到这儿吧。
【ASP.NET Core】标记帮助器——抽象层的更多相关文章
- 【asp.net core 系列】- 11 Service层的实现样板
0.前言 在<asp.net core 系列>之实战系列中,我们在之前的篇幅中对项目有了一个大概的认知,也搭建了一个基础的项目骨架.那么就让我们继续完善这个骨架,让它更加丰满.这一篇,我将 ...
- ASP.NET Core利用拦截器 IActionFilter实现权限控制
“麦荻网教系统”采用了前后端代码分离的架构,即“Miidy.Cloud.Console”站与“Miidy.Cloud.Manage”站(两个前端站)同时通过web api的方式调用“Miidy.Clo ...
- 重学ASP.NET Core 中的标记帮助程序
标记帮助程序是什么 标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素. 例如,内置的 ImageTagHelper 可以将版本号追加到图片名称. 每当图片发生变化时 ...
- C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式
C# -- HttpWebRequest 和 HttpWebResponse 的使用 C# -- HttpWebRequest 和 HttpWebResponse 的使用 结合使用HttpWebReq ...
- 用ASP.NET Core 2.1 建立规范的 REST API -- HATEOAS
本文所需的一些预备知识可以看这里: http://www.cnblogs.com/cgzl/p/9010978.html 和 http://www.cnblogs.com/cgzl/p/9019314 ...
- ASP.Net Core开发(踩坑)指南
ASP.NET与ASP.NET Core很类似,但它们之间存在一些细微区别以及ASP.NET Core中新增特性的使用方法,在此之前也写过一篇简单的对比文章ASP.NET MVC应用迁移到ASP.NE ...
- 在Mac上开发使用yeoman构建Asp.net core项目并且实现分层引用
1.Yeoman? yeoman是一个自动化脚手架工具.它提供很多generator,generator相当于VisualStudio的模板,用来初始化项目.更多的就不多说了,写一遍都写不完,自己看吧 ...
- yoeman构建Asp.net core项目并且实现分层
在Mac上开发使用yoeman构建Asp.net core项目并且实现分层引用 1.Yoeman? yoeman是一个自动化脚手架工具.它提供很多generator,generator相当于Visua ...
- ASP.NET CORE MVC 2.0 项目中引用第三方DLL报错的解决办法 - InvalidOperationException: Cannot find compilation library location for package
目前在学习ASP.NET CORE MVC中,今天看到微软在ASP.NET CORE MVC 2.0中又恢复了允许开发人员引用第三方DLL程序集的功能,感到甚是高兴!于是我急忙写了个Demo想试试,我 ...
- ASP.NET Core gRPC 入门全家桶
一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...
随机推荐
- 基于python的数学建模---轮廓系数的确定
直接上代码 from sklearn import metrics import matplotlib.pyplot as plt from sklearn.cluster import KMeans ...
- i春秋123
打开是个普普通通的登录窗口,下尝试根据提示12341234进行输入,发现不正确...可能1234是指步骤,然后查看源码 发现了绿色的提示信息,我们就根据提示试试打开user.php 打开是白板网页,源 ...
- 重新认识下JVM级别的本地缓存框架Guava Cache(2)——深入解读其容量限制与数据淘汰策略
大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 通过<重新认识下JVM级别的本地 ...
- React综合使用联系
index.js import React from 'react' import ReactDOM from 'react-dom' import CartSimple from './CartSi ...
- 【Spark】Day06-Spark高级课程:性能调优、算子调优、Shuffle调优、JVM调优、数据倾斜、TroubleShooting
一.Spark性能调优 1.常规性能调优 (1)最优资源配置:Executor数量.Executor内存大小.CPU核心数量&Driver内存 (2)RDD优化:RDD复用.RDD持久化(序列 ...
- Linux开发板连接WPA加密的AP路由器
Linux目前有两种方法配置网络: wireless-tools wpa_supplicant iw支持的驱动较多,但只支持WEP加密:wpa_supplicant有部分驱动支持不完善,但支持WEP. ...
- Go语言性能剖析利器--pprof实战
作者:耿宗杰 前言 关于pprof的文章在网上已是汗牛充栋,却是千篇一律的命令介绍,鲜有真正实操的,本文将参考Go社区资料,结合自己的经验,实战Go程序的性能分析与优化过程. 优化思路 首先说一下性能 ...
- java中对象存在形式
本文主要讲述jvm中对象的存储形式: class Cat{ String name; int age; String color; // 行为 } 依据Cat类创建对象 public class Ob ...
- 如何在路由绑定中使用 IParsable
IParsable 是 .Net 7 中新增的接口,它可以将字符串转换为对应的实体.在 Controller 的 Route 绑定中可以使用 IParsable 来绑定复杂的实体. 实验背景 假设有一 ...
- Redis网络模型究竟有多强
如果面试官问我:Redis为什么这么快? 我肯定会说:因为Redis是内存数据库!如果不是直接把数据放在内存里,甭管怎么优化数据结构.设计怎样的网络I/O模型,都不可能达到如今这般的执行效率. 但是这 ...