自定义 .NET Core Tag Helper 不工作
Custom .NET Core Tag Helper not working
我创建了一个新的 ASP.NET 核心 Web 应用程序项目,现在为了简单起见,假设它的 root 命名空间 是 MyWeb.Mvc 这也是项目属性中指定的程序集名称。
在同一个 Web 应用程序项目中,我创建了一个名为 TagHelpers 的文件夹,并在其中添加了一个 class,如下所示:
namespace MyWeb.Mvc.TagHelpers {
public abstract class JsonLdBase : TagHelper
{
protected string addKeyValue(string key, string value, bool noClosing = true)
{
return string.Format("\"{0}\": \"{1}\"{2}{3}", key, value, noClosing ? "," : "", Environment.NewLine);
}
}
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output) {
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("type", new HtmlString("application/ld+json"));
output.Content.SetContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
}
}
然后在 _ViewImports.cshtml 我有这个:
@using MyWeb.Mvc.TagHelpers
@addTagHelper MyWeb.Mvc.TagHelpers.*, MyWeb.Mvc
而在 _Layout.cshtml 我是这样使用它的:
<jsonldwebsite name="Panama Vibes" alt="Panama Vibes" url="PanamaVibes.com"/>
鉴于它被放置在 _Layout.cshtml 中,它应该出现在所有页面中。但是当我编译和 运行 Web 应用程序并查看主页的源代码时,我看到自定义标签已呈现为正常的 HTML 标签,没有经过 TagHelper 处理。
现在,最初我想使用 json-ld NuGet 包,但当我看到它自 2016 年以来没有更新并且程序员没有花时间记录如何使用时我就放弃了使用或给出任何示例。
更新#1
按照建议,我明确设置了 TagMode 属性。然而,整个输出变为 HTML 编码,因此没有通过 JSON+LD 验证。
我现在使用 HtmlString("application/json+ld") 并且类型条目现在可以正确呈现而无需对“+”符号进行编码。但是,我尝试对内容使用相同的方法,但是
您尝试过 HtmlTargetElement 属性吗?
[HtmlTargetElement("jsonldwebsite")]
默认情况下,razor 页面中的标签名称为 json-ld-website
:
<json-ld-website name="Panama Vibes" alt="Panama Vibes" url="PanamaVibes.com" />
您可以使用 HtmlTargetElementAttribute
:
自定义名称
[HtmlTargetElement("jsonldwebsite")]
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("type", "application/ld+json");
output.Content.SetContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
}
此外,您应该将标签的名称更改为 script
,这不是自闭标签。这意味着您应该使用 output.TagMode = TagMode.StartTagAndEndTag;
来确保有一个关闭标签。这样您的示例将生成以下 html:
<script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "Panama Vibes",
"alternateName": "Panama Vibes",
"url": "PanamaVibes.com",
}</script>
编辑
默认情况下对所有值进行编码(出于安全原因)。您可以使用 SetHtmlContent
方法编写 Raw html。对于属性值,您必须提供不对值进行编码的自定义 IHtmlContent。 但是,任何有效的 html 解析器都应该取消对属性值的编码。
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add(new TagHelperAttribute("type", new HtmlContent("application/ld+json")));
output.Content.SetHtmlContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
private class HtmlContent : IHtmlContent
{
private string _value;
public HtmlContent(string value)
{
_value = value;
}
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
writer.Write(_value);
}
}
}
@Meziantou 通过修复 TagMode 和标签命名漏洞让我走上了正确的方向。
然而,编码输出使 JSON+LD 无法通过验证,因为它因 SetContent 中应用的 HTML 编码而变形。在这里我必须做两件事来解决它:
- 在 SetAttribute() 中,我只是用 new HtmlString() 包装了 "application/json+ld",这样加号就这样输出了,而不是经过编码。
- 但是对于内容,无法使用 HtmlString()。但经过一番调查后,我将其更改为将 SetContent() 调用替换为对 output.Content.AppendHtml()
的调用
现在标签已处理,它有一个结束标签,所有 JSON+LD 的格式都正确,因此它通过了验证工具。
对于Microsoft.AspNetCore 2.0 或以上版本
- 自己项目的cs文件中的自定义标签助手
从您的项目 'TagHelper' 文件夹中包含自定义标签助手
在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyProjectNameSpaceInWeb
- 从另一个项目导入自定义标签助手
通过包含最后一行,它将包含主项目中的所有标签助手
如果您将标签助手创建为另一个项目,首先将项目包含在依赖项列表中,并在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyCustomTagHelperProjectNameSpace
- 导入内置标签助手
在项目 'TagHelper' 文件夹中包含内置标签助手
在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
我创建了一个新的 ASP.NET 核心 Web 应用程序项目,现在为了简单起见,假设它的 root 命名空间 是 MyWeb.Mvc 这也是项目属性中指定的程序集名称。
在同一个 Web 应用程序项目中,我创建了一个名为 TagHelpers 的文件夹,并在其中添加了一个 class,如下所示:
namespace MyWeb.Mvc.TagHelpers {
public abstract class JsonLdBase : TagHelper
{
protected string addKeyValue(string key, string value, bool noClosing = true)
{
return string.Format("\"{0}\": \"{1}\"{2}{3}", key, value, noClosing ? "," : "", Environment.NewLine);
}
}
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output) {
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("type", new HtmlString("application/ld+json"));
output.Content.SetContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
}
}
然后在 _ViewImports.cshtml 我有这个:
@using MyWeb.Mvc.TagHelpers
@addTagHelper MyWeb.Mvc.TagHelpers.*, MyWeb.Mvc
而在 _Layout.cshtml 我是这样使用它的:
<jsonldwebsite name="Panama Vibes" alt="Panama Vibes" url="PanamaVibes.com"/>
鉴于它被放置在 _Layout.cshtml 中,它应该出现在所有页面中。但是当我编译和 运行 Web 应用程序并查看主页的源代码时,我看到自定义标签已呈现为正常的 HTML 标签,没有经过 TagHelper 处理。
现在,最初我想使用 json-ld NuGet 包,但当我看到它自 2016 年以来没有更新并且程序员没有花时间记录如何使用时我就放弃了使用或给出任何示例。
更新#1 按照建议,我明确设置了 TagMode 属性。然而,整个输出变为 HTML 编码,因此没有通过 JSON+LD 验证。
我现在使用 HtmlString("application/json+ld") 并且类型条目现在可以正确呈现而无需对“+”符号进行编码。但是,我尝试对内容使用相同的方法,但是
您尝试过 HtmlTargetElement 属性吗?
[HtmlTargetElement("jsonldwebsite")]
默认情况下,razor 页面中的标签名称为 json-ld-website
:
<json-ld-website name="Panama Vibes" alt="Panama Vibes" url="PanamaVibes.com" />
您可以使用 HtmlTargetElementAttribute
:
[HtmlTargetElement("jsonldwebsite")]
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("type", "application/ld+json");
output.Content.SetContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
}
此外,您应该将标签的名称更改为 script
,这不是自闭标签。这意味着您应该使用 output.TagMode = TagMode.StartTagAndEndTag;
来确保有一个关闭标签。这样您的示例将生成以下 html:
<script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "Panama Vibes",
"alternateName": "Panama Vibes",
"url": "PanamaVibes.com",
}</script>
编辑
默认情况下对所有值进行编码(出于安全原因)。您可以使用 SetHtmlContent
方法编写 Raw html。对于属性值,您必须提供不对值进行编码的自定义 IHtmlContent。 但是,任何有效的 html 解析器都应该取消对属性值的编码。
public class JsonLdWebsiteTagHelper : JsonLdBase
{
public string Name { get; set; }
public string Alt { get; set; }
public string Url { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "script";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add(new TagHelperAttribute("type", new HtmlContent("application/ld+json")));
output.Content.SetHtmlContent(
"{" + Environment.NewLine +
addKeyValue("@context", "http://schema.org") +
addKeyValue("@type", "WebSite") +
addKeyValue("name", Name) +
addKeyValue("alternateName", Alt) +
addKeyValue("url", Url) +
"}"
);
}
private class HtmlContent : IHtmlContent
{
private string _value;
public HtmlContent(string value)
{
_value = value;
}
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
writer.Write(_value);
}
}
}
@Meziantou 通过修复 TagMode 和标签命名漏洞让我走上了正确的方向。
然而,编码输出使 JSON+LD 无法通过验证,因为它因 SetContent 中应用的 HTML 编码而变形。在这里我必须做两件事来解决它:
- 在 SetAttribute() 中,我只是用 new HtmlString() 包装了 "application/json+ld",这样加号就这样输出了,而不是经过编码。
- 但是对于内容,无法使用 HtmlString()。但经过一番调查后,我将其更改为将 SetContent() 调用替换为对 output.Content.AppendHtml() 的调用
现在标签已处理,它有一个结束标签,所有 JSON+LD 的格式都正确,因此它通过了验证工具。
对于Microsoft.AspNetCore 2.0 或以上版本
- 自己项目的cs文件中的自定义标签助手
从您的项目 'TagHelper' 文件夹中包含自定义标签助手 在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyProjectNameSpaceInWeb
- 从另一个项目导入自定义标签助手
通过包含最后一行,它将包含主项目中的所有标签助手
如果您将标签助手创建为另一个项目,首先将项目包含在依赖项列表中,并在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyCustomTagHelperProjectNameSpace
- 导入内置标签助手
在项目 'TagHelper' 文件夹中包含内置标签助手 在“_ViewImports.cshtml”文件中使用以下代码
@using MyProjectNameSpaceInWeb
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers