众所周知,在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-

 [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等信息输出。

如下面的写法:

 <form method="post" asp-action="Get" asp-controller="Product" asp-antiforgery="false" asp-route-id="2">
<button type="submit">submit</button>
</form>
生成对应的html如下:
 <form method="post" action="/Product/Get/2">
<button type="submit">submit</button>
</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 -;
}
} 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这个文件

 @using Catcher.EasyDemo.Website
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@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>

Index.cshtml

addTagHelper的用法如下:

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

或者更直接

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

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

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

@removeTagHelper * , Microsoft.AspNetCore.Mvc.TagHelpers

TagHelper是怎么实现的的更多相关文章

  1. ASP.NET Core MVC TagHelper实践HighchartsNET快速图表控件-开源

    ASP.NET Core MVC TagHelper最佳实践HighchartsNET快速图表控件支持ASP.NET Core. 曾经在WebForms上写过 HighchartsNET快速图表控件- ...

  2. 【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右击添加视图,转到试图页等功能图不见了,虽然我 ...

  3. 9.2.3 .net core 通过TagHelper封装控件

    .net core 除了继续保留.net framework的HtmlHelper的写法以外,还提供了TagHelper和ViewComponent方式生成控件. 我们本节说的是使用TagHelper ...

  4. 解读ASP.NET 5 & MVC6系列(13):TagHelper

    在新版的MVC6中,微软提供了强大的TagHelper功能,以便让我们摆脱如下的臃肿代码: @Html.LabelFor(model => model.FullName) @Html.EditF ...

  5. asp.net core的TagHelper简单使用

    TagHelper(标签助手)是ASP.NET Core非常好的一种新特性.可以扩展视图,让其看起来像一个原生HTML标签. 应该使用TagHelper替换HtmlHelper,因其更简洁更易用,且支 ...

  6. [转]【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper

    本文转自:http://www.cnblogs.com/zhangxiaolei521/p/5808417.html 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp ...

  7. ASP.NET 5探险(8):利用中间件、TagHelper来在MVC 6中实现Captcha

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于ASP.NET 5及MVC 6是一个微软全新重新的Web开发平台,之前一些现有的验 ...

  8. TagHelper

    TagHelper是怎么实现的   众所周知,在asp.net core中编写Razor视图的时候,用了一种新的写法--TagHelper 那这个TagHelper是怎么回事呢? 首先来看看TagHe ...

  9. 创建区域Areas,添加TagHelper

    创建区域Areas,添加TagHelper 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右 ...

随机推荐

  1. 前端常用的WindowsCMD命令

    前面的话   在网上找了一些关于命令提示符CMD的资料,但是很多资料都是把所有的功能罗列出来,大部分都不会用到.所以,自己把常用的CMD命令总结如下,方便查阅 操作类 help 列出所有支持的指令及说 ...

  2. Node.js:Buffer浅谈

    Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意.Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该 ...

  3. ASP.NET 5 RC1 升级 ASP.NET Core 1.0 RC2 记录

    升级文档: Migrating from DNX to .NET Core Migrating from ASP.NET 5 RC1 to ASP.NET Core 1.0 RC2 Migrating ...

  4. Log4net - 规则简介

    参考页面: http://www.yuanjiaocheng.net/CSharp/csharprumenshili.html http://www.yuanjiaocheng.net/entity/ ...

  5. animate.css(第三方动画使用方法)

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #a5b2b9 } animation 语法: animatio ...

  6. 如何理解MySQL中auto_increment?

    1.auto_increment用于主键自动增长.比如从1开始增长,当把第一条数据删除,再插入第二条数据时,主键值为2,不是1.

  7. 从史上八大MySQL事故中学到的经验

    本文列举了史上八大MySQL宕机事件原因.影响以及人们从中学到的经验,文中用地震级数来类比宕机事件的严重性和后果,排在最严重层级前两位的是由于亚马逊AWS宕机故障(相当于地震十级和九级). 一.Per ...

  8. 【一起学OpenFOAM】系列由来

    1 为什么要学习OpenFOAM 掐指算起来,接触CFD也差不多有十个年头了,其间一直使用的商用CFD软件,有Fluent.CFX.StarCCM+等,这些商用软件各有其优缺点,都能较好的解决常规的工 ...

  9. 跟Unity3D学代码优化

    今天我们来聊聊如何跟Unity学代码优化,准确地说,是通过学习Unity的IL2CPP技术的优化策略,应用到我们的日常逻辑开发中. 做过Unity开发的同学想必对IL2CPP都很清楚,简单地说,IL2 ...

  10. 利用AOP写2PC框架(一)

    并不是很想写这个系列,因为这个2pc单独写一个小架构有点鸡肋.不过也不知道写什么了,先写了再说吧. 整个流程如下图: 关于AOP系列的文章很多,我这里也再重复造一下轮子. 首先,我们定义了一个IAop ...