TagHelper是怎么实现的

众所周知,在asp.net core中编写Razor视图的时候,用了一种新的写法--TagHelper

那这个TagHelper是怎么回事呢?

 

首先来看看TagHelper的项目位置,它是位于Microsoft.AspNetCore.Mvc.TagHelpers。

如果看到project.json,可以发现,它还依赖一个比较重要的东西Microsoft.AspNetCore.Mvc.Razor

为什么这么说呢,其实很简单,看了里面诸多TagHelper,就会发现,里面都是继承了

Microsoft.AspNetCore.Razor.TagHelpers下面的TagHelper这个抽象类。

下面就以我们天天用到的表单--FormTagHelper为例来说一下,他是怎么实现的。

首先要看看TagHelper这个抽象类:

public abstract class TagHelper : ITagHelper

    {

        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);

    }

面包含两比较重要的方法:Process和ProcessAsync

其实看方法名就应该知道一个是同步的方法一个是异步的方法

因为这个是输出html的方法,你说,这能不重要吗?下面来看看FormTagHelper的具体实现吧!

[HtmlTargetElement("form", Attributes = ActionAttributeName)]

先来看看HtmlTargetElement这个Attribute是用来干嘛的

简单来说,它指定了我们html标签(<form></form>)以及一些相关的元素。

可以看到,诸多Attributes = XXXAttributeName,其中的XXXAttributeName是在类里面定义的变量。

private const string ActionAttributeName = "asp-action";

        private const string AntiforgeryAttributeName = "asp-antiforgery";

        private const string AreaAttributeName = "asp-area";

        private const string ControllerAttributeName = "asp-controller";

        private const string RouteAttributeName = "asp-route";

        private const string RouteValuesDictionaryName = "asp-all-route-data";

        private const string RouteValuesPrefix = "asp-route-";

        private const string HtmlActionAttributeName = "action";

再来看看下面的图,相对比一看,是不是就很清晰了呢?

我们可以看到下面的好几个属性,如Controller,它的上面是有 HtmlAttributeName来标注的

而且这个指向的名字还是ControllerAttributeName(也就是asp-controller)。这个就是用来接收asp-controller的值。

[HtmlAttributeName(ControllerAttributeName)]
public string Controller { get; set; }

相对来说,这样做只是起了个别名。


[HtmlTargetElement("form", Attributes = ActionAttributeName)]

    [HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]

    [HtmlTargetElement("form", Attributes = AreaAttributeName)]

    [HtmlTargetElement("form", Attributes = ControllerAttributeName)]

    [HtmlTargetElement("form", Attributes = RouteAttributeName)]

    [HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]

    [HtmlTargetElement("form", Attributes = RouteValuesPrefix + "*")]

    public class FormTagHelper : TagHelper

当然,我们也是可以不指定别名的,也可以不用在HtmlTargetElement指明Attributes

好比如下的代码,就可以直接用Controller

 [HtmlTargetElement("form")]
public class FormTagHelper : TagHelper
{
public string Controller { get; set; }
}


还有一个RouteValues的属性,它是一个键值对,用来存放参数的,具体可以怎么用呢?

总的来说有两种用法。可以看到它指向asp-all-route-data和asp-route-

1 [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]

用法如下:一种是用asp-all-route-data来接收一个IDictionary类型的变量,一种是通过asp-route-*的方式来接收参数*的值。

这两种写法是等价的。

下面就是FormTagHelper的构造函数和一个Generator属性

 public FormTagHelper(IHtmlGenerator generator)
{
Generator = generator;
 }
protected IHtmlGenerator Generator { get; }

由于在Core中,依赖注入随处可见,看到这个写法马上就是想到了这个

果不其然,发现其对应了一个实现类:DefaultHtmlGenerator。

public class DefaultHtmlGenerator : IHtmlGenerator

    {       

        public DefaultHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ClientValidatorCache clientValidatorCache);    

        public virtual TagBuilder GenerateActionLink(ViewContext viewContext, string linkText, string actionName, string controllerName, string protocol, string hostname, string fragment, object routeValues, object htmlAttributes);

        public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext);    

        public virtual TagBuilder GenerateForm(ViewContext viewContext, string actionName, string controllerName, object routeValues, string method, object htmlAttributes);   

        public virtual TagBuilder GenerateLabel(ViewContext viewContext, ModelExplorer modelExplorer, string expression, string labelText, object htmlAttributes);  

        public virtual TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, int columns, object htmlAttributes);

        public virtual TagBuilder GenerateTextBox(ViewContext viewContext, ModelExplorer modelExplorer, string expression, object value, string format, object htmlAttributes);       

        protected virtual TagBuilder GenerateInput(ViewContext viewContext, InputType inputType, ModelExplorer modelExplorer, string expression, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes);

        protected virtual TagBuilder GenerateLink(string linkText, string url, object htmlAttributes);

     ....省略部分

    }

这个类里面,我们看到了熟悉的TagBuilder,就算不去看它里面的实现都能知道它是用来干嘛的

它就是用来创建我们的Html标签,相信用过MVC的,多多少少都扩展过HtmlHelper,这是类似的。

最后,也是最最重要的重写的Process方法。

可以看到开始就判断了表单<form>中是否包含了action这个属性output.Attributes.ContainsName(HtmlActionAttributeName)

如果包含,就是正常的html标签。换句话说,正常的html写法和我们的TagHelper方法会有冲突,只能用其中一种。

当我们这样写的时候,编译能通过。

但是,运行的时候就会出错。

再下面的处理就是用了TagBuilder去处理了。

收集路由的数据放到一个字典中->区域是否存在->用Generator去创建form表单,返回TagBuilder对象->TagHelperOutput对象把tagbuilder的innerhtml等信息输出。

如下面的写法:

1 <form method="post" asp-action="Get" asp-controller="Product" asp-antiforgery="false" asp-route-id="2">2   <button type="submit">submit</button>3 </form>

生成对应的html如下:

1 <form method="post" action="/Product/Get/2">2   <button type="submit">submit</button>3 </form>

 

到这里,FormTagHelper的讲解就算是OK,至于其他的,原理都是差不多,就不再累赘了。

 

来看看,到底有多少种TagHelper(还没有部分没有列出来),以及它们包含的属性。

 

 

下面是我们自己写一个TagHelper——CatcherATagHelper,这个TagHelper是干什么的呢?它只是一个精简版的A标签。

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.Rendering;

using Microsoft.AspNetCore.Mvc.Routing;

using Microsoft.AspNetCore.Mvc.TagHelpers;

using Microsoft.AspNetCore.Mvc.ViewFeatures;

using Microsoft.AspNetCore.Razor.TagHelpers;


namespace Catcher.EasyDemo.Controllers.TagHelpers

{

    [HtmlTargetElement("catcher-a")]

    public class CatcherATagHelper:TagHelper

    {       

        public CatcherATagHelper(IHtmlGenerator generator, IUrlHelperFactory urlHelperFactory)

        {

            this.Generator = generator;

            UrlHelperFactory = urlHelperFactory;

        }


        [HtmlAttributeNotBound]

        public IUrlHelperFactory UrlHelperFactory { get; }


        protected IHtmlGenerator Generator { get; }

        

        public override int Order

        {

            get

            {

                return -1000;

            }

        }

                

        public string Action { get; set; }

        

        public string Controller { get; set; }


        public string LinkText { get; set; }


        [ViewContext]

        [HtmlAttributeNotBound]

        public ViewContext ViewContext { get; set; }


        public override void Process(TagHelperContext context, TagHelperOutput output)

        {

            //method 1

            if (Action != null || Controller != null)

            {

                output.Attributes.Clear();


                var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);


                output.TagName = "a";


                output.Attributes.SetAttribute("href", urlHelper.Action(Action, Controller));

                //whether the inner html is null

                if (output.Content.IsEmptyOrWhiteSpace)

                {

                    output.PreContent.SetContent(LinkText);

                }

            }

            //method 2

            //TagBuilder tagBuilder;

            //if (Action != null || Controller != null)

            //{

            //    tagBuilder = Generator.GenerateActionLink(

            //            ViewContext,

            //            linkText: string.Empty,

            //            actionName: Action,

            //            controllerName: Controller,

            //            protocol: string.Empty,

            //            hostname: string.Empty,

            //            fragment: string.Empty,

            //            routeValues: null,

            //            htmlAttributes: null);


            //    output.TagName = "a";

            //    //whether the inner html is null

            //    if (output.Content.IsEmptyOrWhiteSpace)

            //    {

            //        output.PreContent.SetContent(LinkText);

            //    }

            //    output.MergeAttributes(tagBuilder);

            //}

        }

    }

}

这里提供了两种写法供大家参考

一种是借助IUrlHelperFactory去生成链接

一种是借助IHtmlGenerator去生成链接

好了之后要怎么用呢?

不知道大家有没有留意_ViewImports.cshtml这个文件

1 @using Catcher.EasyDemo.Website2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers3 @inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration

这个是默认情况下帮我们添加的TagHelper

我们可以在要用到那个TagHelper的地方添加就好

@{
 Layout = null;
}
@addTagHelper Catcher.EasyDemo.Controllers.TagHelpers.CatcherATagHelper , Catcher.EasyDemo.Controllers
<catcher-a action="list" controller="product" link-text="text">With LinkText And InnerHtml</catcher-a>
<br />
<catcher-a action="list" controller="product" link-text="">Without LinkText</catcher-a>
<br />
<catcher-a action="list" controller="product" link-text="Only With LinkText"></catcher-a>

 

addTagHelper的用法如下:

@addTagHelper 你的TagHelper , 你的TagHelper所在的命名空间

或者更直接

@addTagHelper * , 你的TagHelper所在的命名空间 

可以添加,当然也可以删除,删除是@removeTagHelper

当我们在自己的框架中完全重写了一套自己的TagHelper,那么这个时候,微软自己的TagHelper我们就可以通过下面的方法来移除了。

@removeTagHelper * , Microsoft.AspNetCore.Mvc.TagHelpers

原文地址:http://www.cnblogs.com/catcher1994/p/5790720.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java电商面试介绍

写博客 前两个项目采用的是ssh框架搭建的&#xff0c;最近的项目采用的是ssm框架搭建的。在实际开发中&#xff0c;我觉得这两个框架&#xff0c;他们最大的区别在于hibernate与mybatis的区别。 Hibernate与mybatis相比较&#xff0c;mybatis更为轻便、灵活&#xff0c;容易…

java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量

转载自 java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量 之前是在另一位高手的上传内容中学习到的&#xff0c;并将其代码根据我的需求进行了修改&#xff0c;参考位置&#xff1a;http://jiangpin1987.javaeye.com/blog/749690参考代码&#xff1a;…

List<Map<String,Object>>集合按照map中的某个值进行排序

List<Map<String,Object>>集合按照map中的某个值进行排序 举例&#xff1a; public void test5(){List<Map<String, Object>> list new ArrayList<Map<String, Object>>();Map<String, Object> map1 new HashMap<String, Obj…

面试

Java简历与面试 置顶2017年10月20日 10:04:29 阅读数&#xff1a;40142 Java就业指导 想要成为合格的Java程序员或工程师到底需要具备哪些专业技能&#xff0c;面试者在面试之前到底需要准备哪些东西呢&#xff1f;本文陈列的这些内容既可以作为个人简历中的内容&#xff0c…

全文搜索引擎 Elasticsearch 入门教程

转载自 全文搜索引擎 Elasticsearch 入门教程全文搜索属于最常见的需求&#xff0c;开源的 Elasticsearch &#xff08;以下简称 Elastic&#xff09;是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它。Elastic…

写给实习生的第一天

实习生&#xff08;intern&#xff09;和新员工有所区别。实习生仿佛一个长达12周&#xff08;三个月&#xff09;的面试&#xff0c;一起工作&#xff0c;一起解决问题。在最后有答辩和debrief meeting讨论结果。可能通过了&#xff0c;最后公司给offer&#xff1b;也可能没有…

项目里远程调用POST/GET接口时使用的方法

一、远程调用post接口时的方法 方法一&#xff1a; 这个方式的querys传的是参数&#xff08;参数可是单个参数/对象&#xff09; host指的是远程调用的主机地址 headers指的是头信息&#xff08;可有可无&#xff09; body指的是字符集的编码格式 public static String doPo…

再讲IQueryablelt;Tgt;,揭开表达式树的神秘面纱

接上篇《先说IEnumerable&#xff0c;我们每天用的foreach你真的懂它吗&#xff1f;》 最近园子里定制自己的orm那是一个风生水起&#xff0c;感觉不整个自己的orm都不好意思继续混博客园了&#xff08;开个玩笑&#xff09;。那么在此之前我们有必要仔细了解下 IQueryable<…

Java进阶之路——从初级程序员到架构师,从小工到专家

转载自 Java进阶之路——从初级程序员到架构师&#xff0c;从小工到专家怎样学习才能从一名Java初级程序员成长为一名合格的架构师&#xff0c;或者说一名合格的架构师应该有怎样的技术知识体系&#xff0c;这是不仅一个刚刚踏入职场的初级程序员也是工作三五年之后开始迷茫…

堆排序原理及其实现(C++)

https://blog.csdn.net/lzuacm/article/details/52853194 堆排序原理及其实现(C) 1. 堆排序的引入 我们知道简单选择排序的时间复杂度为O(n^2)&#xff0c;熟悉各种排序算法的朋友都知道&#xff0c;这个时间复杂度是很大的&#xff0c;所以怎样减小简单选择排序的时间复杂度…

Redis集群~StackExchange.redis连接Twemproxy代理服务器

本文是Redis集群系列的一篇文章&#xff0c;主要介绍使用StackExchange.Redis进行Twemproxy&#xff08;文中简称TW&#xff09;代理服务的连接过程&#xff0c;事务上&#xff0c;对于&#xff34;&#xff37;来说&#xff0c;我们需要理解一下它的物理架构&#xff0c;它类似…

List集合转分页

当所要查询的数据不能通过框架自带的分页和自定义分页实现的时候&#xff0c;一般查询出来的数据大多数情况都是list的形式。这就牵扯到把list集合转成分页&#xff0c;实现分页操作。   以下就是list转page的工具 /*** list转page进行手动分页* param list 要转的list集合*…

内存转换Image到Icon

转载自 内存转换Image到Icon 时候我们需要在内存中转换Image格式到Icon 根据经验&#xff0c;通常我们应该可以这样做 Image image xxxx;///假设这里已经有一个Image对象 System.IO.MemoryStream mStream new System.IO.MemoryStream();///创建内存流 image.Save(mStream,…

同步异步

https://blog.csdn.net/ideality_hunter/article/details/53453285 同步和异步&#xff0c;区别 2016年12月04日 11:20:17 阅读数&#xff1a;60233 同步&#xff1a; 同步的思想是&#xff1a;所有的操作都做完&#xff0c;才返回给用户。这样用户在线等待的时间太长&…

不想穷下去就请看!

不想穷下去就请看!          1、记住&#xff0c;平均每天看电视超过三个小时以上的&#xff0c;一定都是那些月收入不超过两千元的&#xff0c;如果你想要月收入超过两千&#xff0c;请不要把时间浪费在电视上。同样的道理&#xff0c;那些平均每天玩网络游戏或聊天超过…

.NET开发者如何使用MyCat

背景 在开发中&#xff0c;我们设计的系统的效率在很大程度上会受到数据库引擎单表的性能制约&#xff0c;因此在数据量庞大、或记录过宽时&#xff0c;需要考虑分库分表从而减轻数据库压力&#xff0c;提升查询效率。此外配置读写分离&#xff0c;让主数据库处理事务性查询&am…

关于Icon,Image,ImageIcon的简单的对比参考 上篇

转载自 关于Icon&#xff0c;Image&#xff0c;ImageIcon的简单的对比参考 上篇其实就算是现在&#xff0c;我还是有不少地方概念模糊&#xff0c;但是下面的内容是是没有什么问题的。稍微介绍一下&#xff0c;或许有些地方我无法解释&#xff1a; 大部分内容都是查的Java SE…

移动端为什么拿不到本地运行的web工程数据

原因很简单 连接的是远程服务器的tomcat啊啊啊啊啊

道指mt4代码_剑指offer算法题052:正则表达式匹配

小编在求职找找工作期间剑指offer上的算法题刷了很多遍&#xff0c;并且每道题小编当时都总结了一种最适合面试时手撕算法的最优解法。考虑到剑指offer算法题在面试中的高频出现&#xff0c;小编每天和大家分享一道剑指offer上的算法题&#xff0c;以及小编总结的答案。下面是第…

.NET 程序集单元测试工具 SmokeTest 应用指南

Smoke Test(冒烟测试)&#xff0c;也称Regression Test(回归测试)&#xff0c;是对软件的安装和基本功能的测试。一般地我们使用脚本来实现Smoke Test的自动化&#xff0c;可借用虚拟机的snapshot机制来保证干净的环境来进行Smoke Test&#xff0c;然后将测试好的程序集成到Con…