ASP.NET Core のタグ ヘルパー作成

作成者: Rick Anderson

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

タグ ヘルパーの概要

このチュートリアルでは、タグ ヘルパーのプログラミングの概要を提供します。 タグ ヘルパーの概要に関するページでは、タグ ヘルパーが提供する利点について説明しています。

タグ ヘルパーは、ITagHelper インターフェイス実装する任意のクラスです。 ただし、タグ ヘルパーを作成する場合は、通常は TagHelper から派生させます。こうすることで、Process メソッドにアクセスできるようになります。

  1. AuthoringTagHelpers という新しい ASP.NET Core プロジェクトを作成します。 このプロジェクトに認証は必要ありません。

  2. タグ ヘルパーを保持する TagHelpers というフォルダーを作成します。 TagHelpers フォルダーは必須ではありませんが、あると便利です。 それでは、いくつか単純なタグ ヘルパーを記述してみましょう。

最小のタグ ヘルパー

このセクションでは、電子メール タグを更新するタグ ヘルパーを記述します。 次に例を示します。

<email>Support</email>

サーバーは、電子メール タグ ヘルパーを使用して、そのマークアップを以下に変換します。

<a href="mailto:Support@contoso.com">Support@contoso.com</a>

つまり、これを電子メール リンクにするアンカー タグです。 ブログ エンジンを記述していて、マーケティング、サポート、およびその他の連絡先の電子メールをすべて同じドメインに送信するためにそれが必要な場合に、これを行うことができます。

  1. 次の EmailTagHelper クラスを TagHelpers フォルダーに追加します。

    
    using Microsoft.AspNetCore.Razor.TagHelpers;
    using System.Threading.Tasks;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        public class EmailTagHelper : TagHelper
        {
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                output.TagName = "a";    // Replaces <email> with <a> tag
            }
        }
    }
    
    • タグ ヘルパーは、ルート クラス名の要素 (クラス名部分から TagHelper 部分を引いたもの) をターゲットとする名前付け規則です。 この例では、EmailTagHelper のルート名は email となるため、<email> タグがターゲットとなります。 この命名規則は、ほとんどのタグ ヘルパーで機能します。オーバーライドの方法については、後述します。

    • EmailTagHelper クラスは、TagHelper から派生したものです。 TagHelper クラスはタグ ヘルパーを記述するためのメソッドとプロパティを提供します。

    • オーバーライドされた Process メソッドは、実行時のタグ ヘルパーの動作を制御します。 TagHelper クラスには、同じパラメーターを使用する非同期バージョン (ProcessAsync) も用意されています。

    • Process (および ProcessAsync) のコンテキスト パラメーターには、現在の HTML タグの実行に関連付けられている情報が含まれています。

    • Process (および ProcessAsync) の出力パラメーターには、HTML タグとコンテンツの生成に使用された元のソースを代表するステートフル HTML 要素が含まれています。

    • クラス名には TagHelper のサフィックスが付けられています。これは必須ではありませんが、ベスト プラクティスです。 クラスは、次のように宣言できます。

    public class Email : TagHelper
    
  2. すべての Razor ビューで EmailTagHelper クラスを使用可能にするには、次のように addTagHelper ディレクティブを Views/_ViewImports.cshtml ファイルに追加します。

    @using AuthoringTagHelpers
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @addTagHelper *, AuthoringTagHelpers
    

    上記のコードでは、ワイルドカードの構文を使用して、アセンブリ内のすべてのタグ ヘルパーが使用可能になるように指定しています。 @addTagHelper の後の最初の文字列は、読み込むタグ ヘルパーを指定します (すべてのタグ ヘルパーを指定するには、"*" を使用します)。2 番目の文字列 "AuthoringTagHelpers" は、タグ ヘルパーが存在するアセンブリを指定します。 また、2 行目で、ワイルドカードの構文を使用して ASP.NET Core MVC タグ ヘルパーを取り込むことに注目してください (これらのヘルパーについては、タグ ヘルパーの概要に関するページで説明されています)。Razor ビューでタグ ヘルパーが使用可能になるのは @addTagHelper ディレクティブのためです。 または、次に示すように、タグ ヘルパーの完全修飾名 (FQN) を指定することもできます。

@using AuthoringTagHelpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper AuthoringTagHelpers.TagHelpers.EmailTagHelper, AuthoringTagHelpers

FQN を使用してタグ ヘルパーをビューに追加するには、最初に FQN (AuthoringTagHelpers.TagHelpers.EmailTagHelper) を追加してから、アセンブリ名 (AuthoringTagHelpersnamespace は不要) を追加します。 ほとんどの開発者は、ワイルドカードの構文を使用する方を選びます。 タグ ヘルパーの概要に関するページでは、タグ ヘルパーの追加、削除、階層、およびワイルドカードの構文について詳しく説明されています。

  1. 次の変更で Views/Home/Contact.cshtml ファイル内のマークアップを更新します:

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
  2. アプリを実行して、お好みのブラウザーを使用して HTML ソースを表示すると、電子メール タグがアンカー マークアップ (<a>Support</a> など) に置き換えられていることが確認できます。 SupportMarketing はリンクとして表示されますが、リンクを機能させる href 属性を持っていません。 次のセクションでこれを修正します。

SetAttribute と SetContent

このセクションでは、EmailTagHelper を更新して、電子メール用の有効なアンカー タグが作成できるようにします。 これを更新して、(mail-to 属性の形式で) Razor ビューから情報を取得し、それを使用してアンカーを生成します。

次のように EmailTagHelper クラスを更新します。

public class EmailTagHelper : TagHelper
{
    private const string EmailDomain = "contoso.com";

    // Can be passed via <email mail-to="..." />. 
    // PascalCase gets translated into kebab-case.
    public string MailTo { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "a";    // Replaces <email> with <a> tag

        var address = MailTo + "@" + EmailDomain;
        output.Attributes.SetAttribute("href", "mailto:" + address);
        output.Content.SetContent(address);
    }
}
  • タグ ヘルパーのパスカルケースのクラス名とプロパティ名は、それぞれケバブ ケースに変換されます。 そのため、MailTo 属性を使用するには、相当する <email mail-to="value"/> を使用します。

  • 最後の行は、最低限機能するタグ ヘルパーの完成したコンテンツを設定します。

  • 強調表示されている行は、属性を追加するための構文を示しています。

public override void Process(TagHelperContext context, TagHelperOutput output)
{
    output.TagName = "a";    // Replaces <email> with <a> tag

    var address = MailTo + "@" + EmailDomain;
    output.Attributes.SetAttribute("href", "mailto:" + address);
    output.Content.SetContent(address);
}

この方法は、属性 "href" が属性コレクションに現存していない場合に限り、属性 "href" に対して有効です。 output.Attributes.Add メソッドを使用してタグ ヘルパー属性をタグ属性のコレクションの末尾に追加することもできます。

  1. 次の変更で Views/Home/Contact.cshtml ファイル内のマークアップを更新します:

    @{
        ViewData["Title"] = "Contact Copy";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way Copy Version <br />
        Redmond, WA 98052-6399<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email mail-to="Support"></email><br />
        <strong>Marketing:</strong><email mail-to="Marketing"></email>
    </address>
    
  2. アプリを実行し、適切なリンクが生成されることを確認します。

Note

自己終了の電子メール タグ (<email mail-to="Rick" />) を記述すると、最終的な出力も自己終了になります。 開始タグのみを持つタグ (<email mail-to="Rick">) を記述する機能を有効にするには、次のようにクラスをマークする必要があります。

[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)] 
public class EmailVoidTagHelper : TagHelper
{
    private const string EmailDomain = "contoso.com";
    // Code removed for brevity

自己終了の電子メール タグ ヘルパーでは、出力は <a href="mailto:Rick@contoso.com" /> になります。 自己終了のアンカー タグは有効な HTML ではないため、作成することはないかもしれませんが、自己終了のタグ ヘルパーは必要な場合があります。 タグ ヘルパーは、タグを読み取った後に、TagMode プロパティの型を設定します。

[HtmlAttributeName]属性を使用して、別の属性名をプロパティにマップすることもできます。

recipient という名前の属性を MailTo プロパティにマップするには:

[HtmlAttributeName("recipient")]
public string? MailTo { get; set; }

recipient 属性のタグ ヘルパー:

<email recipient="…"/>

ProcessAsync

このセクションでは非同期の電子メール ヘルパーを記述します。

  1. EmailTagHelper クラスを次のコードで置き換えます。

    public class EmailTagHelper : TagHelper
    {
        private const string EmailDomain = "contoso.com";
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "a";                                 // Replaces <email> with <a> tag
            var content = await output.GetChildContentAsync();
            var target = content.GetContent() + "@" + EmailDomain;
            output.Attributes.SetAttribute("href", "mailto:" + target);
            output.Content.SetContent(target);
        }
    }
    

    注:

    • このバージョンでは、非同期の ProcessAsync メソッドを使用します。 非同期の GetChildContentAsyncTagHelperContent を含む Task を返します。

    • output パラメーターを使用して、HTML 要素のコンテンツを取得します。

  2. Views/Home/Contact.cshtml ファイルに次の変更を行って、タグ ヘルパーがターゲットの電子メールを取得できるようにします。

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
  3. アプリを実行し、有効な電子メール リンクが生成されることを確認します。

RemoveAll、PreContent.SetHtmlContent、PostContent.SetHtmlContent

  1. 次の BoldTagHelper クラスを TagHelpers フォルダーに追加します。

    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        [HtmlTargetElement(Attributes = "bold")]
        public class BoldTagHelper : TagHelper
        {
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                output.Attributes.RemoveAll("bold");
                output.PreContent.SetHtmlContent("<strong>");
                output.PostContent.SetHtmlContent("</strong>");
            }
        }
    }
    
    • [HtmlTargetElement] 属性は、"bold" という名前の HTML 属性を含むすべての HTML 要素を照合することを指定する属性パラメーターを渡し、クラス内で Process オーバーライド メソッドが実行されます。 このサンプルでは、Process メソッドによって "bold" 属性が削除され、中にあるマークアップが <strong></strong> で囲まれます。

    • 既存のタグ コンテンツは置き換えたくないので、PreContent.SetHtmlContent メソッドを使用して開始タグ <strong> を記述し、PostContent.SetHtmlContent メソッドを使用して終了タグ </strong> を記述する必要があります。

  2. bold 属性値を含めるように About.cshtml ビューを変更します。 完成したコードを以下に示します。

    @{
        ViewData["Title"] = "About";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <p bold>Use this area to provide additional information.</p>
    
    <bold> Is this bold?</bold>
    
  3. アプリを実行します。 お好みのブラウザーを使用して、ソースを検査し、マークアップを確認できます。

    上記の [HtmlTargetElement] 属性は、"bold" という属性名を提供する HTML マークアップのみをターゲットにしています。 <bold> 要素は、タグ ヘルパーによって変更されませんでした。

  4. [HtmlTargetElement] 属性の行をコメント アウトすると、既定の <bold> タグ (<bold> 形式の HTML マークアップ) が再度ターゲットになります。 既定の名前付け規則は、クラス名 BoldTagHelper を <bold> タグと一致させることに注意してください。

  5. アプリを実行して、<bold> タグがタグ ヘルパーによって処理されることを確認します。

複数の [HtmlTargetElement] 属性でクラスを修飾すると、結果はターゲットの論理 OR になります。 たとえば、次のコードを使用すると、bold タグまたは bold 属性が一致します。

[HtmlTargetElement("bold")]
[HtmlTargetElement(Attributes = "bold")]
public class BoldTagHelper : TagHelper
{
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.Attributes.RemoveAll("bold");
        output.PreContent.SetHtmlContent("<strong>");
        output.PostContent.SetHtmlContent("</strong>");
    }
}

同じステートメントに複数の属性を追加すると、ランタイムではそれらが論理 AND として扱われます。 たとえば、次のコードでは、一致させるために、"bold" という名前の属性 (<bold bold />) を使って HTML 要素に "bold" と名前を付ける必要があります。

[HtmlTargetElement("bold", Attributes = "bold")]

また、[HtmlTargetElement] を使用してターゲット要素の名前を変更することもできます。 たとえば、BoldTagHelper<MyBold> タグをターゲットにするには、次の属性を使用します。

[HtmlTargetElement("MyBold")]

タグ ヘルパーにモデルを渡す

  1. Models フォルダーを追加します。

  2. 次の WebsiteContext クラスを Models フォルダーに追加します。

    using System;
    
    namespace AuthoringTagHelpers.Models
    {
        public class WebsiteContext
        {
            public Version Version { get; set; }
            public int CopyrightYear { get; set; }
            public bool Approved { get; set; }
            public int TagsToShow { get; set; }
        }
    }
    
  3. 次の WebsiteInformationTagHelper クラスを TagHelpers フォルダーに追加します。

    using System;
    using AuthoringTagHelpers.Models;
    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        public class WebsiteInformationTagHelper : TagHelper
        {
            public WebsiteContext Info { get; set; }
    
          public override void Process(TagHelperContext context, TagHelperOutput output)
          {
             output.TagName = "section";
             output.Content.SetHtmlContent(
    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    <li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
    <li><strong>Approved:</strong> {Info.Approved}</li>
    <li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
             output.TagMode = TagMode.StartTagAndEndTag;
          }
       }
    }
    
    • 前述したように、タグ ヘルパーは、パスカルケースの C# クラス名とタグ ヘルパーのプロパティをケバブ ケースに変換します。 そのため、Razor で WebsiteInformationTagHelper を使用するには、<website-information /> を記述します。

    • [HtmlTargetElement] 属性を使用してターゲット要素を明示的に識別しないため、website-information の既定がターゲットになります。 次の属性を適用した場合 (ケバブ ケースではありませんが、クラス名が一致します):

    [HtmlTargetElement("WebsiteInformation")]
    

    ケバブ ケース タグ <website-information /> は一致しません。 [HtmlTargetElement] 属性を使用する場合は、次に示すようにケバブ ケースを使用します。

    [HtmlTargetElement("Website-Information")]
    
    • 自己終了の要素にはコンテンツがありません。 この例では、Razor マークアップは自己終了タグを使用しますが、タグ ヘルパーは section 要素を作成します (これは自己終了せず、ユーザーによって section 要素内にコンテンツが記述されます)。 そのため、TagModeStartTagAndEndTag に設定して出力を記述する必要があります。 または、TagMode を設定する行をコメント アウトして、終了タグを使ってマークアップを記述することもできます。 (サンプルのマークアップは、後ほどこのチュートリアルで提供します。)

    • 次の行の $ (ドル記号) は、挿入文字列を使用しています。

    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    
  4. 次のマークアップを About.cshtml ビューに追加します。 強調表示されたマークアップは、Web サイトの情報を表示します。

    @using AuthoringTagHelpers.Models
    @{
        ViewData["Title"] = "About";
        WebsiteContext webContext = new WebsiteContext {
                                        Version = new Version(1, 3),
                                        CopyrightYear = 1638,
                                        Approved = true,
                                        TagsToShow = 131 };
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <p bold>Use this area to provide additional information.</p>
    
    <bold> Is this bold?</bold>
    
    <h3> web site info </h3>
    <website-information info="webContext" />
    

    Note

    Razor マークアップを次に示します。

    <website-information info="webContext" />
    

    Razor は info 属性を文字列ではなくクラスとして認識し、ユーザーは C# コードを記述します。 文字列以外のタグ ヘルパー属性はすべて、@ 文字を使わずに記述する必要があります。

  5. アプリを実行し、[バージョン情報] ビューに移動して、Web サイトの情報を確認します。

    Note

    終了タグを持つ次のマークアップを使用して、タグ ヘルパー内の TagMode.StartTagAndEndTag を持つ行を削除できます。

    <website-information info="webContext" >
    </website-information>
    

条件タグ ヘルパー

条件タグ ヘルパーは、true 値が渡されたときに出力をレンダリングします。

  1. 次の ConditionTagHelper クラスを TagHelpers フォルダーに追加します。

    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        [HtmlTargetElement(Attributes = nameof(Condition))]
        public class ConditionTagHelper : TagHelper
        {
            public bool Condition { get; set; }
    
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                if (!Condition)
                {
                    output.SuppressOutput();
                }
            }
        }
    }
    
  2. Views/Home/Index.cshtml ファイルの内容を次のマークアップに置き換えます。

    @using AuthoringTagHelpers.Models
    @model WebsiteContext
    
    @{
        ViewData["Title"] = "Home Page";
    }
    
    <div>
        <h3>Information about our website (outdated):</h3>
        <Website-InforMation info="Model" />
        <div condition="Model.Approved">
            <p>
                This website has <strong surround="em">@Model.Approved</strong> been approved yet.
                Visit www.contoso.com for more information.
            </p>
        </div>
    </div>
    
  3. Home コントローラーの Index メソッドを次のコードで置き換えます。

    public IActionResult Index(bool approved = false)
    {
        return View(new WebsiteContext
        {
            Approved = approved,
            CopyrightYear = 2015,
            Version = new Version(1, 3, 3, 7),
            TagsToShow = 20
        });
    }
    
  4. アプリを実行して、ホーム ページに移動します。 条件付き div ではマークアップはレンダリングされません。 クエリ文字列 ?approved=true を URL に付加します (例: http://localhost:1235/Home/Index?approved=true)。 approved は true に設定され、条件付きマークアップが表示されます。

Note

nameof 演算子を使用して、太字タグ ヘルパーで行ったように文字列を指定するのではなく、ターゲットにする属性を指定します。

[HtmlTargetElement(Attributes = nameof(Condition))]
 //   [HtmlTargetElement(Attributes = "condition")]
 public class ConditionTagHelper : TagHelper
{
   public bool Condition { get; set; }

   public override void Process(TagHelperContext context, TagHelperOutput output)
   {
      if (!Condition)
      {
         output.SuppressOutput();
      }
   }
}

nameof 演算子は、コードがリファクタリングされないように保護します (名前は RedCondition に変更できます)。

タグ ヘルパーの競合を回避する

このセクションでは、自動リンク タグ ヘルパーのペアを記述します。 1 つ目のヘルパーは HTTP で始まる URL を含むマークアップを、同じ URL を含む HTML アンカー タグに置き換えます (この URL へのリンクも生成されます)。 2 つ目のヘルパーは WWW で始まる URL に対して同じことを行います。

これら 2 つのヘルパーは密接に関連しており、将来的にそれらをリファクターする可能性があるため、これらを同じファイルで保持します。

  1. 次の AutoLinkerHttpTagHelper クラスを TagHelpers フォルダーに追加します。

    [HtmlTargetElement("p")]
    public class AutoLinkerHttpTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var childContent = await output.GetChildContentAsync();
            // Find Urls in the content and replace them with their anchor tag equivalent.
            output.Content.SetHtmlContent(Regex.Replace(
                 childContent.GetContent(),
                 @"\b(?:https?://)(\S+)\b",
                  "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
        }
    }
    

    Note

    AutoLinkerHttpTagHelper クラスは p 要素をターゲットとし、Regex を使用してアンカーを作成します。

  2. Views/Home/Contact.cshtml ファイルの末尾に次のマークアップを追加します:

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
    <p>Visit us at http://docs.asp.net or at www.microsoft.com</p>
    
  3. アプリを実行し、タグ ヘルパーがアンカーを正しくレンダリングすることを確認します。

  4. AutoLinker クラスを更新して AutoLinkerWwwTagHelper を含めます。これは www テキストを元の www テキストも含むアンカー タグに変換します。 以下では、更新されたコードが強調表示されています。

        [HtmlTargetElement("p")]
        public class AutoLinkerHttpTagHelper : TagHelper
        {
            public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
            {
                var childContent = await output.GetChildContentAsync();
                // Find Urls in the content and replace them with their anchor tag equivalent.
                output.Content.SetHtmlContent(Regex.Replace(
                     childContent.GetContent(),
                     @"\b(?:https?://)(\S+)\b",
                      "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
            }
        }
    
        [HtmlTargetElement("p")]
        public class AutoLinkerWwwTagHelper : TagHelper
        {
            public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
            {
                var childContent = await output.GetChildContentAsync();
                // Find Urls in the content and replace them with their anchor tag equivalent.
                output.Content.SetHtmlContent(Regex.Replace(
                    childContent.GetContent(),
                     @"\b(www\.)(\S+)\b",
                     "<a target=\"_blank\" href=\"http://$0\">$0</a>"));  // www version
            }
        }
    }
    
  5. アプリを実行します。 www テキストはリンクとしてレンダリングされていますが、HTTP テキストはそのようになっていないことに注目してください。 両方のクラスにブレークポイントを配置すると、HTTP タグ ヘルパー クラスが最初に実行されることがわかります。 問題は、タグ ヘルパーの出力がキャッシュされ、WWW タグ ヘルパーを実行するときに、WWW タグ ヘルパーが HTTP タグ ヘルパーからのキャッシュされた出力を上書きすることです。 タグ ヘルパーの実行順序を制御する方法については、このチュートリアルで後ほど説明します。 コードを次のように修正します。

      public class AutoLinkerHttpTagHelper : TagHelper
      {
          public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
          {
              var childContent = output.Content.IsModified ? output.Content.GetContent() :
                  (await output.GetChildContentAsync()).GetContent();
    
              // Find Urls in the content and replace them with their anchor tag equivalent.
              output.Content.SetHtmlContent(Regex.Replace(
                   childContent,
                   @"\b(?:https?://)(\S+)\b",
                    "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
          }
      }
    
      [HtmlTargetElement("p")]
      public class AutoLinkerWwwTagHelper : TagHelper
      {
          public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
          {
              var childContent = output.Content.IsModified ? output.Content.GetContent() : 
                  (await output.GetChildContentAsync()).GetContent();
    
              // Find Urls in the content and replace them with their anchor tag equivalent.
              output.Content.SetHtmlContent(Regex.Replace(
                   childContent,
                   @"\b(www\.)(\S+)\b",
                   "<a target=\"_blank\" href=\"http://$0\">$0</a>"));  // www version
          }
      }
    

    Note

    自動リンク タグ ヘルパーの最初のエディションで、次のコードを使用してターゲットのコンテンツを取得しました。

    var childContent = await output.GetChildContentAsync();
    

    つまり、ProcessAsync メソッドに渡された TagHelperOutput を使用して GetChildContentAsync を呼び出します。 前述したように、出力はキャッシュされるため、最後に実行されたタグ ヘルパーによって上書きされます。 次のコードを使用してこの問題を修正しました。

    var childContent = output.Content.IsModified ? output.Content.GetContent() : 
        (await output.GetChildContentAsync()).GetContent();
    

    上記のコードは、コンテンツが変更されているかどうかをチェックし、変更されている場合は、出力バッファーからコンテンツを取得します。

  6. アプリを実行して、2 つのリンクが期待どおりに動作することを確認します。 自動リンカー タグ ヘルパーが正しく完全に機能しているように見えても、微妙な問題があります。 WWW タグ ヘルパーを最初に実行すると、www リンクが不正確になります。 Order オーバーロードを追加してコードを更新し、タグの実行順序を制御します。 Order プロパティを、同じ要素をターゲットとする他のタグ ヘルパーと比べることで実行順序が決まります。 既定の順序の値は 0 で、より低い値を持つインスタンスの方が先に実行されます。

    public class AutoLinkerHttpTagHelper : TagHelper
    {
        // This filter must run before the AutoLinkerWwwTagHelper as it searches and replaces http and 
        // the AutoLinkerWwwTagHelper adds http to the markup.
        public override int Order
        {
            get  {  return int.MinValue;   }
        }
    

    上記のコードにより、WWW タグ ヘルパーの前に、HTTP タグ ヘルパーが実行されることが保証されます。 OrderMaxValue に変更し、WWW タグの生成されたマークアップが正しいことを確認します。

子コンテンツを検査して取得する

タグ ヘルパーには、コンテンツを取得するための複数のプロパティが用意されています。

  • GetChildContentAsync の結果を output.Content に付加できます。
  • GetContent を使用して GetChildContentAsync の結果を検査できます。
  • output.Content を変更すると、この自動リンカー サンプルのように、GetChildContentAsync を呼び出すまで、TagHelper ボディは実行またはレンダリングされません。
public class AutoLinkerHttpTagHelper : TagHelper
{
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var childContent = output.Content.IsModified ? output.Content.GetContent() :
            (await output.GetChildContentAsync()).GetContent();

        // Find Urls in the content and replace them with their anchor tag equivalent.
        output.Content.SetHtmlContent(Regex.Replace(
             childContent,
             @"\b(?:https?://)(\S+)\b",
              "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
    }
}
  • GetChildContentAsync を複数回呼び出すと、同じ値が返され、キャッシュされた結果を使用しないことを示す false パラメーターを渡さない限り、TagHelper ボディは再実行されません。

縮小された部分ビュー TagHelper を読み込む

運用環境では、縮小された部分ビューを読み込むことでパフォーマンスを向上できます。 運用環境で縮小された部分ビューを活用するには:

  • 部分ビューを縮小するビルド前プロセスを作成/設定します。
  • 次のコードを使って、非開発環境に縮小された部分ビューを読み込みます。
public class MinifiedVersionPartialTagHelper : PartialTagHelper
    {
        public MinifiedVersionPartialTagHelper(ICompositeViewEngine viewEngine, 
                                IViewBufferScope viewBufferScope)
                               : base(viewEngine, viewBufferScope)
        {

        }

        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            // Append ".min" to load the minified partial view.
            if (!IsDevelopment())
            {
                Name += ".min";
            }

            return base.ProcessAsync(context, output);
        }

        private bool IsDevelopment()
        {
            return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") 
                                                 == EnvironmentName.Development;
        }
    }