所谓标记帮助器,就是针对 HTML 标签(不管是标准的还是自己命名的)进行扩展的做法。它是以 Razor 为基础的,服务于开发人员的。在服务器端用 C# 代码来实现一些需求,并生成 HTML 元素。在 Razor 文档中可以方便书写,VS 、VS Code 等工具还有提示功能。
老周这篇水文不介绍常用的标记帮助器,毕竟这些大伙们都会用,就是在 Razor 文档中用 @addTagHeler 指令导入的那些类型。如内置的 input、form 元素的帮助器。像咱们常用的像 asp-controller 、asp-action 这些HTML属性就是通过帮助器来扩充的。
咱们先聊最抽象的接口:ITagHelperComponent。咦?这货还真是以“Component”结尾,看来确实把标记帮助器认定为一种小型 Razor 组件。看看这接口为我们规范了些啥。
数值越小就越先被执行,比如,0、3、5,那么,Order 为0的先执行,Order为5的后执行。
ProcessAsync 方法:这个是核核核心心心,重要的事延长三拍。各种为 HTML 元素添加属性、生成内容等都在此方法中完成。
[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>元素。
[HtmlTargetElement("p"] public class PragTagHelper : ITagHelper { …… }
然后再次运行。哟西,这下起作用了。
public interface ITagHelper : ITagHelperComponent { }
这样就可以得出结论:ITagHelper 接口是一个标记接口,用来筛选出哪些类型可以用 @addTagHelper 指令引入——即哪些类型被认为是标记帮助器。
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>"; } }
来,咱们用例子来说明。
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#要求左"""后要换行,右"""前也要换行。这样规定可能是为了写起代码来好看。
@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 文档有效。
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 样式。
好了,今天咱们就聊到这儿吧。