Auxiliares de marca de autor no ASP.NET Core

De Rick Anderson

Exibir ou baixar código de exemplo (como baixar)

Introdução aos Auxiliares de Marca

Este tutorial fornece uma introdução à programação de auxiliares de marcação. Introdução aos auxiliares de marcação descreve os benefícios que os auxiliares de marcação fornecem.

Um auxiliar de marca é qualquer classe que implementa a interface ITagHelper. No entanto, quando cria um auxiliar de marca, você geralmente deriva de TagHelper, o que fornece acesso ao método Process.

  1. Crie um novo projeto do ASP.NET Core chamado AuthoringTagHelpers. Você não precisará de autenticação para esse projeto.

  2. Criar uma pasta para armazenar os auxiliares de marca chamados TagHelpers. A pasta TagHelpers pasta não é necessária, mas é uma convenção comum. Agora vamos começar a escrever alguns auxiliares de marca simples.

Um auxiliar de marca mínimo

Nesta seção, você escreve um auxiliar de marca que atualiza uma marca de email. Por exemplo:

<email>Support</email>

O servidor usará nosso auxiliar de marca de email para converter essa marcação como a seguir:

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

Ou seja, uma marca de âncora que torna isso um link de email. Talvez você deseje fazer isso se estiver escrevendo um mecanismo de blog e precisar que ele envie emails para o marketing, suporte e outros contatos, todos para o mesmo domínio.

  1. Adicione a classe EmailTagHelper a seguir à pasta 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
            }
        }
    }
    
    • Auxiliares de marcação usam uma convenção de nomenclatura que tem como alvo os elementos do nome da classe raiz (menos o TagHelper parte do nome de classe). Neste exemplo, o nome da raiz EmailTagHelper é email e, portanto, a marca <email> será direcionada. Essa convenção de nomenclatura deve funcionar para a maioria dos auxiliares de marcação, posteriormente, mostrarei como substituí-la.

    • A classe EmailTagHelper deriva de TagHelper. O TagHelper classe fornece métodos e propriedades para gravar tag helpers.

    • O método Process substituído controla o que o auxiliar de marca faz quando é executado. A classe TagHelper também fornece uma versão assíncrona (ProcessAsync) com os mesmos parâmetros.

    • O parâmetro de contexto para Process (e ProcessAsync) contém informações associadas à execução da marca HTML atual.

    • O parâmetro de saída para Process (e ProcessAsync) contém um elemento HTML com estado que representa a fonte original usada para gerar uma marca HTML e o conteúdo.

    • Nosso nome de classe tem um sufixo TagHelper, que não é necessário, mas que é considerado uma convenção de melhor prática. Você pode declarar a classe como:

    public class Email : TagHelper
    
  2. Para disponibilizar a classe EmailTagHelper para todas as nossas exibições do Razor, adicione a diretiva addTagHelper ao arquivo Views/_ViewImports.cshtml:

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

    O código acima usa a sintaxe de curinga para especificar que todos os auxiliares de marca em nosso assembly estarão disponíveis. A primeira cadeia de caracteres após @addTagHelper especifica o auxiliar de marca a ser carregado (use "*" para todos os auxiliares de marca) e a segunda cadeia de caracteres "AuthoringTagHelpers" especifica o assembly no qual o auxiliar de marca se encontra. Além disso, observe que a segunda linha insere os auxiliares de marca do ASP.NET Core MVC usando a sintaxe de curinga (esses auxiliares são abordados em Introdução ao auxiliares de marcação). É a diretiva @addTagHelper que torna o auxiliar de marcação disponível para exibição do Razor. Como alternativa, você pode fornecer o FQN (nome totalmente qualificado) de um auxiliar de marca, conforme mostrado abaixo:

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

Para adicionar um auxiliar de marca para uma exibição usando um FQN, primeiro adicione o FQN (AuthoringTagHelpers.TagHelpers.EmailTagHelper) e, em seguida, o nome do assembly (AuthoringTagHelpers, não necessariamente o namespace). A maioria dos desenvolvedores vão preferir usar a sintaxe de curinga. Introdução ao tag helpers apresenta detalhes sobre a sintaxe de adição, remoção, hierarquia e curinga do tag helper.

  1. Atualize a marcação no arquivo Views/Home/Contact.cshtml com essas alterações:

    @{
        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. Execute o aplicativo e use seu navegador favorito para exibir o código-fonte HTML para verificar se as marcas de email são substituídas pela marcação de âncora (por exemplo, <a>Support</a>). Suporte e Marketing são renderizados como links, mas não têm um atributo href para torná-los funcionais. Corrigiremos isso na próxima seção.

SetAttribute e SetContent

Nesta seção, atualizaremos o EmailTagHelper para que ele crie uma marca de âncora válida para email. Vamos atualizá-lo para obter informações de uma exibição do Razor (na forma de um atributo mail-to) e usar isso na geração da âncora.

Atualize a classe EmailTagHelper com o seguinte:

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);
    }
}
  • Nomes de classe e de propriedade na formatação Pascal Case para auxiliares de marcações são convertidos em Kebab Case. Portanto, para usar o atributo MailTo, você usará o equivalente de <email mail-to="value"/>.

  • A última linha define o conteúdo concluído para nosso tag helper minimamente funcional.

  • A linha realçada mostra a sintaxe para adicionar atributos:

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);
}

Essa abordagem funciona para o atributo "href" como no momento, ele não existe na coleção de atributos. Você também pode usar o output.Attributes.Add para adicionar um atributo do tag helper ao final da coleção de atributos de marca.

  1. Atualize a marcação no arquivo Views/Home/Contact.cshtml com essas alterações:

    @{
        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. Execute o aplicativo e verifique se ele gera os links corretos.

Observação

Se você pretende escrever o autofechamento da marca de email (<email mail-to="Rick" />), a saída final também é o autofechamento. Para permitir a capacidade de gravar a marca com uma marca de início (<email mail-to="Rick">), é necessário marcar a classe com o seguinte:

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

Com um auxiliar de marca da marca de email com autofechamento, a saída será <a href="mailto:Rick@contoso.com" />. As marcas de âncora com autofechamento são um HTML inválido. Portanto, não é recomendável criá-las, mas talvez criar um auxiliar de marca com autofechamento. Auxiliares de marca definem o tipo da propriedade TagMode após a leitura de uma marca.

Você também pode mapear um nome de atributo diferente para uma propriedade usando o atributo [HtmlAttributeName] .

Para mapear um atributo chamado recipient para a propriedade MailTo:

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

Auxiliar de Marcação para o atributo recipient:

<email recipient="…"/>

ProcessAsync

Nesta seção, escreveremos um auxiliar de email assíncrono.

  1. Substitua a classe EmailTagHelper pelo seguinte código:

    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);
        }
    }
    

    Observações:

    • Essa versão usa o método ProcessAsync assíncrono. O GetChildContentAsync assíncrono retorna uma Task que contém o TagHelperContent.

    • Use o parâmetro output para obter o conteúdo do elemento HTML.

  2. Faça a alteração a seguir no arquivo Views/Home/Contact.cshtml para que o auxiliar de marca possa obter o email de destino.

    @{
        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. Execute o aplicativo e verifique se ele gera links de email válidos.

RemoveAll, PreContent.SetHtmlContent e PostContent.SetHtmlContent

  1. Adicione a classe BoldTagHelper a seguir à pasta 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>");
            }
        }
    }
    
    • O atributo [HtmlTargetElement] passa um parâmetro de atributo que especifica que qualquer elemento HTML que contenha um atributo HTML nomeado "bold" terá uma correspondência e que o método de substituição Process na classe será executado. Em nossa amostra, o método Process remove o atributo "bold" e envolve a marcação contida com <strong></strong>.

    • Como você não deseja substituir a conteúdo de marca existente, você precisa escrever a marca <strong> de abertura com o método PreContent.SetHtmlContent e a marca </strong> de fechamento com o método PostContent.SetHtmlContent.

  2. Modifique a exibição About.cshtml para que ela contenha um valor de atributo bold. O código completo é mostrado abaixo.

    @{
        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. Execute o aplicativo. Use seu navegador favorito para inspecionar a origem e verificar a marcação.

    O [HtmlTargetElement] atributo acima se destina somente a marcação HTML que fornece um nome de atributo de "bold". O <bold> elemento não foi modificado pelo tag helper.

  4. Comente a linha de atributo [HtmlTargetElement] e ela usará como padrão as marcações <bold> de direcionamento, ou seja, a marcação HTML do formato <bold>. Lembre-se de que a convenção de nomenclatura padrão fará a correspondência do nome da classe BoldTagHelper com as marcações <bold>.

  5. Execute o aplicativo e verifique se a marca <bold> é processada pelo auxiliar de marca.

A decoração de uma classe com vários atributos [HtmlTargetElement] resulta em um OR lógico dos destinos. Por exemplo, o uso do código a seguir, uma marca de negrito ou um atributo de negrito terá uma correspondência.

[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>");
    }
}

Quando vários atributos são adicionados à mesma instrução, o runtime trata-os como um AND lógico. Por exemplo, no código abaixo, um elemento HTML precisa ser nomeado "bold" com um atributo nomeado "bold" (<bold bold />) para que haja a correspondência.

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

Também use o [HtmlTargetElement] para alterar o nome do elemento de destino. Por exemplo, se você deseja que o BoldTagHelper seja direcionado a marcações <MyBold>, use o seguinte atributo:

[HtmlTargetElement("MyBold")]

Passe um model para um tag helper

  1. Adicionar uma pasta models.

  2. Adicione a seguinte classe WebsiteContext à pasta 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. Adicione a classe WebsiteInformationTagHelper a seguir à pasta 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;
          }
       }
    }
    
    • Conforme mencionado anteriormente, os auxiliares de marcações convertem nomes de classes e de propriedades dos auxiliares de marcações do C# na formatação Pascal Case em Kebab Case. Portanto, para usar o WebsiteInformationTagHelper no Razor, você escreverá <website-information />.

    • Você não identifica de forma explícita o elemento de destino com o atributo [HtmlTargetElement]. Portanto, o padrão de website-information será o destino. Se você aplicou o seguinte atributo (observe que não está em kebab case, mas corresponde ao nome da classe):

    [HtmlTargetElement("WebsiteInformation")]
    

    A marcação <website-information /> em Kebab Case não terá uma correspondência. Caso deseje usar o atributo [HtmlTargetElement], use o kebab case, conforme mostrado abaixo:

    [HtmlTargetElement("Website-Information")]
    
    • Os elementos com autofechamento não têm nenhum conteúdo. Para este exemplo, a marcação do Razor usará uma marca com autofechamento, mas o auxiliar de marca criará um elemento section (que não tem autofechamento e você escreve o conteúdo dentro do elemento section). Portanto, você precisa definir TagMode como StartTagAndEndTag para escrever a saída. Como alternativa, você pode comentar a linha definindo TagMode e escrever a marcação com uma marca de fechamento. (A marcação de exemplo é fornecida mais adiante neste tutorial.)

    • O $ (cifrão) na seguinte linha usa uma cadeia de caracteres interpolada:

    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    
  4. Adicione a seguinte marcação na exibição About.cshtml. A marcação realçada exibe as informações do site.

    @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" />
    

    Observação

    Na marcação do Razor mostrada abaixo:

    <website-information info="webContext" />
    

    O Razor reconhece que o atributo info é uma classe, e não uma cadeia de caracteres, bem como que você deseja escrever o código C#. Qualquer atributo do auxiliar de marca que não seja uma cadeia de caracteres deve ser escrito sem o caractere @.

  5. Execute o aplicativo e navegue para a exibição About sobre para ver as informações do site.

    Observação

    Você pode usar a seguinte marcação com uma marca de fechamento e remova a linha com TagMode.StartTagAndEndTag no tag helper:

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

Tag helper condicional

O auxiliar de marca de condição renderiza a saída quando recebe um valor true.

  1. Adicione a classe ConditionTagHelper a seguir à pasta 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. Substitua o conteúdo do arquivo Views/Home/Index.cshtml pela seguinte marcação:

    @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. Substitua o método Index no controlador Home pelo seguinte código:

    public IActionResult Index(bool approved = false)
    {
        return View(new WebsiteContext
        {
            Approved = approved,
            CopyrightYear = 2015,
            Version = new Version(1, 3, 3, 7),
            TagsToShow = 20
        });
    }
    
  4. Execute o aplicativo e navegue para a home page. A marcação no div condicional não será renderizada. Acrescente a cadeia de caracteres de consulta ?approved=true à URL (por exemplo, http://localhost:1235/Home/Index?approved=true). approved é definido como verdadeiro e a marcação condicional será exibida.

Observação

Use o nameof operador para especificar o atributo de destino em vez de especificar uma cadeia de caracteres como você fez com o tag helper em negrito:

[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();
      }
   }
}

O operador nameof protegerá o código, caso ele seja refatorado (recomendamos alterar o nome para RedCondition).

Evitar conflitos de tag helper

Nesta seção, você escreve um par de tag helpers de vinculação automática. O primeiro substituirá a marcação que contém uma URL iniciada por HTTP para um HTML âncora marca que contém a mesma URL (e, portanto, resultando em um link de URL). O segundo fará o mesmo para uma URL começando com WWW.

Como esses dois auxiliares estão intimamente relacionados e você poderá refatorá-los no futuro, vamos mantê-los no mesmo arquivo.

  1. Adicione a classe AutoLinkerHttpTagHelper a seguir à pasta 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}
        }
    }
    

    Observação

    A classe AutoLinkerHttpTagHelper é direcionada a elementos p e usa o Regex para criar a âncora.

  2. Adicione a seguinte marcação ao final do arquivo 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. Execute o aplicativo e verifique se o tag helper renderiza a âncora corretamente.

  4. Atualize a classe AutoLinker para incluir o AutoLinkerWwwTagHelper que converterá o texto www em uma marca de âncora que também contém o texto www original. O código atualizado é realçado abaixo:

        [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. Execute o aplicativo. Observe que o texto www é renderizado como um link, ao contrário do texto HTTP. Se você colocar um ponto de interrupção em ambas as classes, poderá ver que a classe do auxiliar de marca HTTP é executada primeiro. O problema é que a saída do auxiliar de marca é armazenada em cache e quando o auxiliar de marca WWW é executado, ele substitui a saída armazenada em cache do auxiliar de marca HTTP. Mais adiante no tutorial, veremos como controlar a ordem na qual os auxiliares de marca são executados. Corrigiremos o código com o seguinte:

      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
          }
      }
    

    Observação

    Na primeira edição os tag helpers de vinculação automática, você obteve o conteúdo do destino com o código a seguir:

    var childContent = await output.GetChildContentAsync();
    

    Ou seja, você chama GetChildContentAsync usando a TagHelperOutput passada para o método ProcessAsync. Conforme mencionado anteriormente, como a saída é armazenada em cache, o último auxiliar de marca a ser executado vence. Você corrigiu o problema com o seguinte código:

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

    O código acima verifica se o conteúdo foi modificado e, em caso afirmativo, ele obtém o conteúdo do buffer de saída.

  6. Execute o aplicativo e verifique se os dois links funcionam conforme esperado. Embora possa parecer que nosso auxiliar de marca de vinculador automático está correto e completo, ele tem um problema sutil. Se o auxiliar de marca WWW for executado primeiro, os links www não estarão corretos. Atualize o código adicionando a sobrecarga Order para controlar a ordem em que a marca é executada. A propriedade Order determina a ordem de execução em relação aos outros auxiliares de marca direcionados ao mesmo elemento. O valor de ordem padrão é zero e as instâncias com valores mais baixos são executadas primeiro.

    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;   }
        }
    

    O código acima garante que o auxiliar de marca HTTP seja executado antes do auxiliar de marca WWW. Altere Order para MaxValue e verifique se a marcação gerada para a marcação WWW está incorreta.

Inspecionar e recuperar o conteúdo filho

Os tag helpers fornecem várias propriedades para recuperar o conteúdo.

  • O resultado de GetChildContentAsync pode ser acrescentado ao output.Content.
  • Inspecione o resultado de GetChildContentAsync com GetContent.
  • Se você modificar output.Content, o corpo da TagHelper não será executado ou renderizado, a menos que você chame GetChildContentAsync como em nossa amostra de vinculador automático:
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}
    }
}
  • Várias chamadas a GetChildContentAsync retornam o mesmo valor e não executam o corpo TagHelper novamente, a menos que você passe um parâmetro falso indicando para não usar o resultado armazenado em cache.

Carregar a exibição parcial minimizada TagHelper

Em ambientes de produção, o desempenho pode ser melhorado com o carregamento de exibições parciais minimizadas. Para tirar proveito da exibição parcial minimizada em produção:

  • Crie/configure um processo de pré-compilação que minimize as exibições parciais.
  • Use o seguinte código para carregar as exibições parciais minimizadas em ambientes que não sejam de desenvolvimento.
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;
        }
    }