TagHelper是怎么实现的
眾所周知,在asp.net core中編寫Razor視圖的時(shí)候,用了一種新的寫法--TagHelper
那這個(gè)TagHelper是怎么回事呢?
?
首先來看看TagHelper的項(xiàng)目位置,它是位于Microsoft.AspNetCore.Mvc.TagHelpers。
如果看到project.json,可以發(fā)現(xiàn),它還依賴一個(gè)比較重要的東西Microsoft.AspNetCore.Mvc.Razor
為什么這么說呢,其實(shí)很簡(jiǎn)單,看了里面諸多TagHelper,就會(huì)發(fā)現(xiàn),里面都是繼承了
Microsoft.AspNetCore.Razor.TagHelpers下面的TagHelper這個(gè)抽象類。
下面就以我們天天用到的表單--FormTagHelper為例來說一下,他是怎么實(shí)現(xiàn)的。
首先要看看TagHelper這個(gè)抽象類:
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
其實(shí)看方法名就應(yīng)該知道一個(gè)是同步的方法一個(gè)是異步的方法
因?yàn)檫@個(gè)是輸出html的方法,你說,這能不重要嗎?下面來看看FormTagHelper的具體實(shí)現(xiàn)吧!
[HtmlTargetElement("form", Attributes = ActionAttributeName)]先來看看HtmlTargetElement這個(gè)Attribute是用來干嘛的
簡(jiǎn)單來說,它指定了我們html標(biāo)簽(<form></form>)以及一些相關(guān)的元素。
可以看到,諸多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";
再來看看下面的圖,相對(duì)比一看,是不是就很清晰了呢?
我們可以看到下面的好幾個(gè)屬性,如Controller,它的上面是有 HtmlAttributeName來標(biāo)注的
而且這個(gè)指向的名字還是ControllerAttributeName(也就是asp-controller)。這個(gè)就是用來接收asp-controller的值。
[HtmlAttributeName(ControllerAttributeName)]public string Controller { get; set; }
相對(duì)來說,這樣做只是起了個(gè)別名。
[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
當(dāng)然,我們也是可以不指定別名的,也可以不用在HtmlTargetElement指明Attributes
好比如下的代碼,就可以直接用Controller
[HtmlTargetElement("form")]public class FormTagHelper : TagHelper
{
public string Controller { get; set; }
}
還有一個(gè)RouteValues的屬性,它是一個(gè)鍵值對(duì),用來存放參數(shù)的,具體可以怎么用呢?
總的來說有兩種用法。可以看到它指向asp-all-route-data和asp-route-
1 [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]用法如下:一種是用asp-all-route-data來接收一個(gè)IDictionary類型的變量,一種是通過asp-route-*的方式來接收參數(shù)*的值。
這兩種寫法是等價(jià)的。
下面就是FormTagHelper的構(gòu)造函數(shù)和一個(gè)Generator屬性
public FormTagHelper(IHtmlGenerator generator){
Generator = generator;
?}
protected IHtmlGenerator Generator { get; }
由于在Core中,依賴注入隨處可見,看到這個(gè)寫法馬上就是想到了這個(gè)
果不其然,發(fā)現(xiàn)其對(duì)應(yīng)了一個(gè)實(shí)現(xiàn)類: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);
....省略部分
? ? }
這個(gè)類里面,我們看到了熟悉的TagBuilder,就算不去看它里面的實(shí)現(xiàn)都能知道它是用來干嘛的
它就是用來創(chuàng)建我們的Html標(biāo)簽,相信用過MVC的,多多少少都擴(kuò)展過HtmlHelper,這是類似的。
最后,也是最最重要的重寫的Process方法。
可以看到開始就判斷了表單<form>中是否包含了action這個(gè)屬性output.Attributes.ContainsName(HtmlActionAttributeName)
如果包含,就是正常的html標(biāo)簽。換句話說,正常的html寫法和我們的TagHelper方法會(huì)有沖突,只能用其中一種。
當(dāng)我們這樣寫的時(shí)候,編譯能通過。
但是,運(yùn)行的時(shí)候就會(huì)出錯(cuò)。
再下面的處理就是用了TagBuilder去處理了。
收集路由的數(shù)據(jù)放到一個(gè)字典中->區(qū)域是否存在->用Generator去創(chuàng)建form表單,返回TagBuilder對(duì)象->TagHelperOutput對(duì)象把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>生成對(duì)應(yīng)的html如下:
1 <form method="post" action="/Product/Get/2">2 <button type="submit">submit</button>3 </form>?
到這里,FormTagHelper的講解就算是OK,至于其他的,原理都是差不多,就不再累贅了。
?
來看看,到底有多少種TagHelper(還沒有部分沒有列出來),以及它們包含的屬性。
?
?
下面是我們自己寫一個(gè)TagHelper——CatcherATagHelper,這個(gè)TagHelper是干什么的呢?它只是一個(gè)精簡(jiǎn)版的A標(biāo)簽。
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這個(gè)文件
1 @using Catcher.EasyDemo.Website2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers3 @inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration這個(gè)是默認(rèn)情況下幫我們添加的TagHelper
我們可以在要用到那個(gè)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所在的命名空間?
可以添加,當(dāng)然也可以刪除,刪除是@removeTagHelper
當(dāng)我們?cè)谧约旱目蚣苤型耆貙懥艘惶鬃约旱腡agHelper,那么這個(gè)時(shí)候,微軟自己的TagHelper我們就可以通過下面的方法來移除了。
@removeTagHelper * , Microsoft.AspNetCore.Mvc.TagHelpers
原文地址:http://www.cnblogs.com/catcher1994/p/5790720.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的TagHelper是怎么实现的的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据项目实践:基于hadoop+spa
- 下一篇: 再讲IQueryablelt;Tgt;,