经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
【ASP.NET Core】标记帮助器——抽象层
来源:cnblogs  作者:东邪独孤  时间:2023/2/20 15:19:33  对本文有异议

标记帮助器,即 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 指令导入的。咱们来做来试验。

  1. [HtmlTargetElement("p")]
  2. public class PragTagHelper : ITagHelperComponent
  3. {
  4. public int Order => 2; //这个优先级可以随意
  5.  
  6. public void Init(TagHelperContext context)
  7. {
  8. // 不用写代码
  9. }
  10. public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
  11. {
  12. // 内容之前
  13. output.PreContent.SetHtmlContent("<strong>");
  14. // 内容之后
  15. output.PostContent.SetHtmlContent("</strong>");
  16. return Task.CompletedTask;
  17. }
  18. }
[HtmlTargetElement("p")] 特性表示:我这个标记帮助器是专为<p>元素准备,它只作用于此元素。上述例子的意思是在<p>元素的内部文本呈现之前插入“<strong>”,在内部文本呈现之后插入“</strong>”。就是实现了让段落中的文本加粗显示的效果。PreContent 表示元素内容之前,PostContent 表示元素内容之后。
现在,咱们在 Razor 文档用 @addTagHelper 指令导入一下。
  1. @page
  2. @addTagHelper TestApp.PragTagHelper, TestApp
  3. <p>孔明用枪打死了王司徒</p>
  4.  
  5. <p>孔明用手雷轰死了王朗</p>

运行程序后,发现不起作用。生成的 HTML 文档没有插入<strong>元素。

然后,我把标记帮助器的代码改一改。这次咱们不实现 ITagHelperComponent 接口,而是 ITagHelper 接口。

  1. [HtmlTargetElement("p")]
  2. public class PragTagHelper : ITagHelper
  3. {
  4. ……
  5. }

然后再次运行。哟西,这下起作用了。

嗯,看来 ITagHelper 接口里面有文章,从声明可以看到,这个接口是继承 ITagHelperComponent 接口的。但这个接口是空的,没定义新成员。

  1. public interface ITagHelper : ITagHelperComponent
  2. {
  3. }

这样就可以得出结论:ITagHelper 接口是一个标记接口,用来筛选出哪些类型可以用 @addTagHelper 指令引入——即哪些类型被认为是标记帮助器。

为了方便开发者定义自己的标记帮助器,ASP.NET Core 还提供了一个抽象类 TagHelper。

  1. public abstract class TagHelper : ITagHelper, ITagHelperComponent
  2. {
  3. // 构造函数
  4. protected TagHelper();
  5. public virtual int Order { get; }
  6. public virtual void Init(TagHelperContext context);
  7. public virtual void Process(TagHelperContext context, TagHelperOutput output);
  8. public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
  9. }

这个抽象类将接口的实现成员都声明为虚方法,派生时开发者可以按需重写。于是,咱们前面那个例子可以做以下修改:

  1. [HtmlTargetElement("p")]
  2. public class PragTagHelper : TagHelper
  3. {
  4. public override void Process(TagHelperContext context, TagHelperOutput output)
  5. {
  6. // 内容之前
  7. output.PreContent.SetHtmlContent("<strong>");
  8. // 内容之后
  9. output.PostContent.SetHtmlContent("</strong>");
  10. }
  11. }

 

咱们前面试过,ITagHelperComponent 的实现类是不能被 @addTagHelper 指令发现的,那么,这个接口还有没有用呢?当然有用,只是直接实现这个接口的类,只针对<head>和<body>元素,通常用于大面积修改 HTML 的情形。比如,你要在 <body> 元素中插入一段 js 脚本,插入一堆HTML元素,插入一段CSS样式等。

来,咱们用例子来说明。

  1. public class InsertStylesTagHelper : ITagHelperComponent
  2. {
  3. public int Order => 105;
  4. public void Init(TagHelperContext context)
  5. {
  6. // 空白
  7. }
  8. public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
  9. {
  10. // 要判断一下是不是<head>元素
  11. if(output.TagName.Equals("head", StringComparison.OrdinalIgnoreCase))
  12. {
  13. // 插入以下CSS
  14. string css = """
  15. <style>
  16. h5 {
  17. color: blue;
  18. }
  19. h3 {
  20. color: green;
  21. font-style: italic;
  22. }
  23. p[setfont] {
  24. font-family: '楷体';
  25. }
  26. </style>
  27. """;
  28. output.PreContent.AppendHtml(css);
  29. }
  30. return Task.CompletedTask;
  31. }
  32. }

这个帮助器就是在 <head> 元素内容的前面插入一段 supper style,不,是 CSS。因为CSS是一大段文本,这里老周用到了 C# 的原义文本块(就是不转义特殊字符),这个功能和 Python 中的差不多。只是C#要求左"""后要换行,右"""前也要换行。这样规定可能是为了写起代码来好看。

这种不实现 ITagHelper 的类型不能用 @addTagHelper 指令来引入,而是要添加到 ITagHelperComponentManager 接口的 Components 属性中,此属性是个列表对象,可以Add。

ITagHelperComponentManager 类有个内部实现的类叫 TagHelperComponentManager,这个类没有对外公开,但不影响我们使用。当我们在服务容器上开启 MVC、RazorPages 等功能时,会自动向容器注册 ITagHelperComponentManager 。因此,在 Razor 文档中,咱们可以通过依赖注入获取它,然后把自己定义的 TagHelper 放进 Components 列表即可。
  1. @page
  2. @using Microsoft.AspNetCore.Mvc.Razor.TagHelpers
  3. @using TestApp
  4. @inject ITagHelperComponentManager tagHelperManager
  5. @{
  6. // 手动添加TagHelper组件
  7. var mytaghelper = new InsertStylesTagHelper();
  8. // 添加到组件列表中
  9. tagHelperManager.Components.Add(mytaghelper);
  10. }
  11. <html>
  12. <head>
  13. <title>好看的例子</title>
  14. <meta charset="UTF-8" />
  15. </head>
  16. <body>
  17. <h3>三号标题</h3>
  18. <h5>五号标题</h5>
  19. <h2>二号标题 - 此处不应用样式</h2>
  20. <p>其他内容 - 不应用样式</p>
  21. <p setfont>使用楷书字体</p>
  22. </body>
  23. </html>

这样一弄,在运行程序后,自定义的 InsertStylesTagHelper 类就会自动应用到 head 标记上。

 

还没完呢,接下来咱们偷窥一下 TagHelperComponentManager 类的源代码。

  1. internal sealed class TagHelperComponentManager : ITagHelperComponentManager
  2. {
  3. /// <summary>
  4. /// Creates a new <see cref="TagHelperComponentManager"/>.
  5. /// </summary>
  6. /// <param name="tagHelperComponents">The collection of <see cref="ITagHelperComponent"/>s.</param>
  7. public TagHelperComponentManager(IEnumerable<ITagHelperComponent> tagHelperComponents)
  8. {
  9. if (tagHelperComponents == null)
  10. {
  11. throw new ArgumentNullException(nameof(tagHelperComponents));
  12. }
  13. Components = new List<ITagHelperComponent>(tagHelperComponents);
  14. }
  15. /// <inheritdoc />
  16. public ICollection<ITagHelperComponent> Components { get; }
  17. }

其实这代码没啥好看的,只要注意它的构造函数就行了。不知道你看到这个构造函数想到了啥,老周想到了依赖注入。什么意思?就是说:你把实现 ITagHelperComponent 接口的类都注册为服务,那么,它就会自动起作用了,而且是面向整个应用程序的 Razor 代码。刚才咱们用依赖注入获取 ITagHelperComponentManager,并手动添加标记帮助器对象的方法是局部的,只对当前 Razor 文档有效。

所以,下面咱们把自己写的 InsertStylesTagHelper 注册为服务。

  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddRazorPages();
  3. builder.Services.AddTransient<ITagHelperComponent, InsertStylesTagHelper>();
  4. var app = builder.Build();

最后回到 Razor 文档,打扫一下,手动添加到 Components 列表的代码现在不需要了。

  1. @page
  2. <html>
  3. <head>
  4. <title>好看的例子</title>
  5. <meta charset="UTF-8" />
  6. </head>
  7. <body>
  8. <h3>三号标题</h3>
  9. <h5>五号标题</h5>
  10. <h2>二号标题 - 此处不应用样式</h2>
  11. <p>其他内容 - 不应用样式</p>
  12. <p setfont>使用楷书字体</p>
  13. </body>
  14. </html>

再次运行一下,你会发现也是可行的,<head>元素内也插入了 CSS 样式。

 

好了,今天咱们就聊到这儿吧。

 

原文链接:https://www.cnblogs.com/tcjiaan/p/17132372.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号