标签:
众所周知,在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这个抽象类:
1 public abstract class TagHelper : ITagHelper
2 {
3 protected TagHelper();
4 public virtual int Order { get; }
5 public virtual void Init(TagHelperContext context);
6 public virtual void Process(TagHelperContext context, TagHelperOutput output);
7 public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
8 }
里面包含两比较重要的方法:Process和ProcessAsync
其实看方法名就应该知道一个是同步的方法一个是异步的方法
因为这个是输出html的方法,你说,这能不重要吗?下面来看看FormTagHelper的具体实现吧!
1 [HtmlTargetElement("form", Attributes = ActionAttributeName)]
简单来说,它指定了我们html标签(<form></form>)以及一些相关的元素。
可以看到,诸多Attributes = XXXAttributeName,其中的XXXAttributeName是在类里面定义的变量。
1 private const string ActionAttributeName = "asp-action"; 2 private const string AntiforgeryAttributeName = "asp-antiforgery"; 3 private const string AreaAttributeName = "asp-area"; 4 private const string ControllerAttributeName = "asp-controller"; 5 private const string RouteAttributeName = "asp-route"; 6 private const string RouteValuesDictionaryName = "asp-all-route-data"; 7 private const string RouteValuesPrefix = "asp-route-"; 8 private const string HtmlActionAttributeName = "action";
再来看看下面的图,相对比一看,是不是就很清晰了呢?

我们可以看到下面的好几个属性,如Controller,它的上面是有 HtmlAttributeName来标注的
而且这个指向的名字还是ControllerAttributeName(也就是asp-controller)。这个就是用来接收asp-controller的值。
1 [HtmlAttributeName(ControllerAttributeName)]
2 public string Controller { get; set; }
1 [HtmlTargetElement("form", Attributes = ActionAttributeName)]
2 [HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
3 [HtmlTargetElement("form", Attributes = AreaAttributeName)]
4 [HtmlTargetElement("form", Attributes = ControllerAttributeName)]
5 [HtmlTargetElement("form", Attributes = RouteAttributeName)]
6 [HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]
7 [HtmlTargetElement("form", Attributes = RouteValuesPrefix + "*")]
8 public class FormTagHelper : TagHelper
好比如下的代码,就可以直接用Controller
1 [HtmlTargetElement("form")]
2 public class FormTagHelper : TagHelper
3 {
4 public string Controller { get; set; }
5 }

总的来说有两种用法。可以看到它指向asp-all-route-data和asp-route-
1 [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]

用法如下:一种是用asp-all-route-data来接收一个IDictionary类型的变量,一种是通过asp-route-*的方式来接收参数*的值。
下面就是FormTagHelper的构造函数和一个Generator属性
1 public FormTagHelper(IHtmlGenerator generator)
2 {
3 Generator = generator;
4 }
5 protected IHtmlGenerator Generator { get; }
果不其然,发现其对应了一个实现类:DefaultHtmlGenerator。
1 public class DefaultHtmlGenerator : IHtmlGenerator
2 {
3 public DefaultHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ClientValidatorCache clientValidatorCache);
4 public virtual TagBuilder GenerateActionLink(ViewContext viewContext, string linkText, string actionName, string controllerName, string protocol, string hostname, string fragment, object routeValues, object htmlAttributes);
5 public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext);
6 public virtual TagBuilder GenerateForm(ViewContext viewContext, string actionName, string controllerName, object routeValues, string method, object htmlAttributes);
7 public virtual TagBuilder GenerateLabel(ViewContext viewContext, ModelExplorer modelExplorer, string expression, string labelText, object htmlAttributes);
8 public virtual TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, int columns, object htmlAttributes);
9 public virtual TagBuilder GenerateTextBox(ViewContext viewContext, ModelExplorer modelExplorer, string expression, object value, string format, object htmlAttributes);
10 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);
11 protected virtual TagBuilder GenerateLink(string linkText, string url, object htmlAttributes);
12 ....省略部分
13 }
它就是用来创建我们的Html标签,相信用过MVC的,多多少少都扩展过HtmlHelper,这是类似的。
最后,也是最最重要的重写的Process方法。
如果包含,就是正常的html标签。换句话说,正常的html写法和我们的TagHelper方法会有冲突,只能用其中一种。
当我们这样写的时候,编译能通过。

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

再下面的处理就是用了TagBuilder去处理了。
如下面的写法:
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>
1 <form method="post" action="/Product/Get/2"> 2 <button type="submit">submit</button> 3 </form>
下面是我们自己写一个TagHelper——CatcherATagHelper,这个TagHelper是干什么的呢?它只是一个精简版的A标签。
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.AspNetCore.Mvc.Rendering;
3 using Microsoft.AspNetCore.Mvc.Routing;
4 using Microsoft.AspNetCore.Mvc.TagHelpers;
5 using Microsoft.AspNetCore.Mvc.ViewFeatures;
6 using Microsoft.AspNetCore.Razor.TagHelpers;
7
8 namespace Catcher.EasyDemo.Controllers.TagHelpers
9 {
10 [HtmlTargetElement("catcher-a")]
11 public class CatcherATagHelper:TagHelper
12 {
13 public CatcherATagHelper(IHtmlGenerator generator, IUrlHelperFactory urlHelperFactory)
14 {
15 this.Generator = generator;
16 UrlHelperFactory = urlHelperFactory;
17 }
18
19 [HtmlAttributeNotBound]
20 public IUrlHelperFactory UrlHelperFactory { get; }
21
22 protected IHtmlGenerator Generator { get; }
23
24 public override int Order
25 {
26 get
27 {
28 return -1000;
29 }
30 }
31
32 public string Action { get; set; }
33
34 public string Controller { get; set; }
35
36 public string LinkText { get; set; }
37
38 [ViewContext]
39 [HtmlAttributeNotBound]
40 public ViewContext ViewContext { get; set; }
41
42 public override void Process(TagHelperContext context, TagHelperOutput output)
43 {
44 //method 1
45 if (Action != null || Controller != null)
46 {
47 output.Attributes.Clear();
48
49 var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
50
51 output.TagName = "a";
52
53 output.Attributes.SetAttribute("href", urlHelper.Action(Action, Controller));
54 //whether the inner html is null
55 if (output.Content.IsEmptyOrWhiteSpace)
56 {
57 output.PreContent.SetContent(LinkText);
58 }
59 }
60 //method 2
61 //TagBuilder tagBuilder;
62 //if (Action != null || Controller != null)
63 //{
64 // tagBuilder = Generator.GenerateActionLink(
65 // ViewContext,
66 // linkText: string.Empty,
67 // actionName: Action,
68 // controllerName: Controller,
69 // protocol: string.Empty,
70 // hostname: string.Empty,
71 // fragment: string.Empty,
72 // routeValues: null,
73 // htmlAttributes: null);
74
75 // output.TagName = "a";
76 // //whether the inner html is null
77 // if (output.Content.IsEmptyOrWhiteSpace)
78 // {
79 // output.PreContent.SetContent(LinkText);
80 // }
81 // output.MergeAttributes(tagBuilder);
82 //}
83 }
84 }
85 }
这里提供了两种写法供大家参考
一种是借助IUrlHelperFactory去生成链接
一种是借助IHtmlGenerator去生成链接
不知道大家有没有留意_ViewImports.cshtml这个文件
1 @using Catcher.EasyDemo.Website 2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 @inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration
这个是默认情况下帮我们添加的TagHelper
我们可以在要用到那个TagHelper的地方添加就好

Index.cshtml
@addTagHelper 你的TagHelper , 你的TagHelper所在的命名空间
@addTagHelper * , 你的TagHelper所在的命名空间
可以添加,当然也可以删除,删除是@removeTagHelper
当我们在自己的框架中完全重写了一套自己的TagHelper,那么这个时候,微软自己的TagHelper我们就可以通过下面的方法来移除了。
@removeTagHelper * , Microsoft.AspNetCore.Mvc.TagHelpers
标签:
原文地址:http://www.cnblogs.com/Leo_wl/p/5797874.html