Crear asistentes de etiquetas en ASP.NET Core

Por Rick Anderson

Vea o descargue el código de ejemplo (cómo descargarlo)

Introducción a los asistentes de etiquetas

En este tutorial se proporciona una introducción a la programación de asistentes de etiquetas. En Introducción a los asistentes de etiquetas se describen las ventajas que proporcionan los asistentes de etiquetas.

Un asistente de etiquetas es una clase que implementa la interfaz ITagHelper. A pesar de ello, cuando se crea un asistente de etiquetas, normalmente se deriva de TagHelper, lo que da acceso al método Process.

  1. Cree un proyecto de ASP.NET Core denominado AuthoringTagHelpers. No necesita autenticación para este proyecto.

  2. Cree una carpeta para almacenar los asistentes de etiquetas denominada TagHelpers. La carpeta TagHelpersno es necesaria, pero es una convención razonable. Ahora vamos a empezar a escribir algunos asistentes de etiquetas simples.

Asistente de etiquetas mínima

En esta sección, escribirá un asistente de etiquetas que actualice una etiqueta de correo electrónico. Por ejemplo:

<email>Support</email>

El servidor usará nuestro asistente de etiquetas de correo electrónico para convertir ese marcado en lo siguiente:

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

Es decir, una etiqueta delimitadora lo convierte en un vínculo de correo electrónico. Tal vez le interese si está escribiendo un motor de blogs y necesita que envíe correos electrónicos a contactos de marketing, soporte técnico y de otro tipo, todos ellos en el mismo dominio.

  1. Agregue la siguiente clase EmailTagHelper a la carpeta 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
            }
        }
    }
    
    • Los asistentes de etiquetas usan una convención de nomenclatura que tiene como destino los elementos de la clase raíz (menos la parte TagHelper del nombre de clase). En este ejemplo, el nombre raíz de EmailTagHelper es email, por lo que el destino será la etiqueta <email>. Esta convención de nomenclatura debería funcionar para la mayoría de los asistentes de etiquetas. Más adelante veremos cómo invalidarla.

    • La clase EmailTagHelper deriva de TagHelper. La clase TagHelper proporciona métodos y propiedades para escribir asistentes de etiquetas.

    • El método Process invalidado controla lo que hace el asistente de etiquetas cuando se ejecuta. La clase TagHelper también proporciona una versión asincrónica (ProcessAsync) con los mismos parámetros.

    • El parámetro de contexto para Process (y ProcessAsync) contiene información relacionada con la ejecución de la etiqueta HTML actual.

    • El parámetro de salida para Process (y ProcessAsync) contiene un elemento HTML con estado que representa el origen original usado para generar una etiqueta y contenido HTML.

    • El nombre de nuestra clase tiene un sufijo TagHelper, que no es necesario, pero es una convención recomendada. Podría declarar la clase de la manera siguiente:

    public class Email : TagHelper
    
  2. Para hacer que la clase EmailTagHelper esté disponible para todas nuestras vistas de Razor, agregue la directiva addTagHelper al archivo Views/_ViewImports.cshtml:

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

    El código anterior usa la sintaxis de comodines para especificar que todos los asistentes de etiquetas del ensamblado estarán disponibles. La primera cadena después de @addTagHelper especifica el asistente de etiquetas que se va a cargar (use "*" para todos los asistentes de etiquetas), mientras que la segunda cadena "AuthoringTagHelpers" especifica el ensamblado en el que se encuentra el asistente de etiquetas. Además, tenga en cuenta que la segunda línea incorpora los asistentes de etiquetas de ASP.NET Core MVC mediante la sintaxis de comodines (esos asistentes se tratan en el tema Introducción a los asistentes de etiquetas). La directiva @addTagHelper hace que el asistente de etiquetas esté disponible en la vista Razor. Como alternativa, puede proporcionar el nombre completo (FQN) de un asistente de etiquetas como se muestra a continuación:

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

Para agregar un asistente de etiquetas a una vista con un FQN, agregue primero el FQN (AuthoringTagHelpers.TagHelpers.EmailTagHelper) y, después, el nombre del ensamblado (AuthoringTagHelpers, no necesariamente namespace). La mayoría de los desarrolladores prefiere usar la sintaxis de comodines. En Introducción a los asistentes de etiquetas se describe en detalle la adición y eliminación de asistentes de etiquetas, la jerarquía y la sintaxis de comodines.

  1. Actualización del marcado en el archivo Views/Home/Contact.cshtml con estos cambios:

    @{
        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. Ejecute la aplicación y use su explorador favorito para ver el código fuente HTML, a fin de comprobar que las etiquetas de correo electrónico se han reemplazado por un marcado delimitador (por ejemplo, <a>Support</a>). Support y Marketing se representan como vínculos, pero no tienen un atributo href que los haga funcionales. Esto lo corregiremos en la sección siguiente.

SetAttribute y SetContent

En esta sección, actualizaremos EmailTagHelper para que cree una etiqueta delimitadora válida para correo electrónico. Lo actualizaremos para que tome información de una vista de Razor(en forma de atributo mail-to) y la use al generar el delimitador.

Actualice la clase EmailTagHelper con lo siguiente:

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);
    }
}
  • Los nombres de clase y propiedad con grafía Pascal para los asistentes de etiquetas se convierten a su grafía kebab. Por tanto, para usar el atributo MailTo, usará su equivalente <email mail-to="value"/>.

  • La última línea establece el contenido completado para nuestro asistente de etiquetas mínimamente funcional.

  • La línea resaltada muestra la sintaxis para agregar 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);
}

Este enfoque funciona para el atributo "href" siempre y cuando no exista actualmente en la colección de atributos. También puede usar el método output.Attributes.Add para agregar un atributo del asistente de etiquetas al final de la colección de atributos de etiqueta.

  1. Actualización del marcado en el archivo Views/Home/Contact.cshtml con estos cambios:

    @{
        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. Ejecute la aplicación y compruebe que genera los vínculos correctos.

Nota:

Si escribe la etiqueta de correo electrónico como de autocierre (<email mail-to="Rick" />), la salida final también será de autocierre. Para habilitar la capacidad de escribir la etiqueta únicamente con una etiqueta de apertura (<email mail-to="Rick">), debe marcar la clase con lo siguiente:

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

Con un asistente de etiquetas de correo electrónico de autocierre, el resultado sería <a href="mailto:Rick@contoso.com" />. Las etiquetas delimitadoras de autocierre no son HTML válido, por lo que no le interesa crear una, pero tal vez le convenga crear un asistente de etiquetas de autocierre. Los asistentes de etiquetas establecen el tipo de la propiedad TagMode después de leer una etiqueta.

También puede asignar un nombre de atributo diferente a una propiedad mediante el atributo [HtmlAttributeName].

Para asignar un atributo denominado recipient a la propiedad MailTo:

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

Asistente de etiquetas para el atributo recipient:

<email recipient="…"/>

ProcessAsync

En esta sección, escribiremos un asistente de correo electrónico asincrónico.

  1. Reemplace la clase EmailTagHelper por el siguiente 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);
        }
    }
    

    Notas:

    • Esta versión usa el método ProcessAsync asincrónico. El método GetChildContentAsync asincrónico devuelve un valor Task que contiene TagHelperContent.

    • Use el parámetro output para obtener el contenido del elemento HTML.

  2. Realice el cambio siguiente en el archivo Views/Home/Contact.cshtml para que el asistente de etiquetas pueda obtener el correo electrónico 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. Ejecute la aplicación y compruebe que genera vínculos de correo electrónico válidos.

RemoveAll, PreContent.SetHtmlContent y PostContent.SetHtmlContent

  1. Agregue la siguiente clase BoldTagHelper a la carpeta 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>");
            }
        }
    }
    
    • El atributo [HtmlTargetElement] pasa un parámetro de atributo que especifica que todos los elementos HTML que contengan un atributo HTML denominado "bold" coincidirán, y se ejecutará el método de invalidación Process de la clase. En nuestro ejemplo, el método Process quita el atributo "bold" y rodea el marcado contenedor con <strong></strong>.

    • Dado que no le interesa reemplazar el contenido existente de la etiqueta, debe escribir la etiqueta de apertura <strong> con el método PreContent.SetHtmlContent y la etiqueta de cierre </strong> con el método PostContent.SetHtmlContent.

  2. Modifique la vista About.cshtml para que contenga un valor de atributo bold. A continuación se muestra el código completado.

    @{
        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. Ejecutar la aplicación. Puede usar el explorador que prefiera para inspeccionar el origen y comprobar el marcado.

    El atributo [HtmlTargetElement] anterior solo tiene como destino el marcado HTML que proporciona el nombre de atributo "bold". El asistente de etiquetas no ha modificado el elemento <bold>.

  4. Convierta en comentario la línea de atributo [HtmlTargetElement] y de forma predeterminada tendrá como destino las etiquetas <bold>, es decir, el marcado HTML con formato <bold>. Recuerde que la convención de nomenclatura predeterminada hará coincidir el nombre de clase BoldTagHelper con las etiquetas <bold>.

  5. Ejecute la aplicación y compruebe que el asistente de etiquetas procesa la etiqueta <bold>.

El proceso de decorar una clase con varios atributos [HtmlTargetElement] tiene como resultado una operación OR lógica de los destinos. Por ejemplo, si se usa el código siguiente, una etiqueta bold o un atributo bold coincidirán.

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

Cuando se agregan varios atributos a la misma instrucción, el tiempo de ejecución los trata como una operación AND lógica. Por ejemplo, en el código siguiente, un elemento HTML debe denominarse "bold" con un atributo denominado "bold" (<bold bold />) para que coincida.

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

También puede usar [HtmlTargetElement] para cambiar el nombre del elemento de destino. Por ejemplo, si quiere que BoldTagHelper tenga como destino etiquetas <MyBold>, use el atributo siguiente:

[HtmlTargetElement("MyBold")]

Pasar un modelo a un asistente de etiquetas

  1. Agregue una carpeta Models.

  2. Agregue la clase WebsiteContext siguiente a la carpeta 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. Agregue la siguiente clase WebsiteInformationTagHelper a la carpeta 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;
          }
       }
    }
    
    • Como se ha indicado anteriormente, los asistentes de etiquetas convierten las propiedades y nombres de clase de C# con grafía Pascal para asistentes de etiquetas en grafía kebab. Por tanto, para usar WebsiteInformationTagHelper en Razor, deberá escribir <website-information />.

    • No está identificando de manera explícita el elemento de destino con el atributo [HtmlTargetElement], por lo que el destino será el valor predeterminado de website-information. Si ha aplicado el atributo siguiente (tenga en cuenta que no tiene grafía kebab, pero coincide con el nombre de clase):

    [HtmlTargetElement("WebsiteInformation")]
    

    La etiqueta con grafía kebab <website-information /> no coincidiría. Si quiere usar el atributo [HtmlTargetElement], debe usar la grafía kebab como se muestra a continuación:

    [HtmlTargetElement("Website-Information")]
    
    • Los elementos que son de autocierre no tienen contenido. En este ejemplo, el marcado de Razor usará una etiqueta de autocierre, pero el asistente de etiquetas creará un elemento section (que no es de autocierre, y el contenido se escribirá dentro del elemento section). Por tanto, debe establecer TagMode en StartTagAndEndTag para escribir la salida. Como alternativa, puede convertir en comentario la línea donde se establece TagMode y escribir marcado con una etiqueta de cierre. (Más adelante en este tutorial se proporciona marcado de ejemplo).

    • El signo de dólar $ de la línea siguiente usa una cadena interpolada:

    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    
  4. Agregue el marcado siguiente en la vista About.cshtml. En el marcado resaltado se muestra la información del sitio 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" />
    

    Nota:

    En el marcado de Razor que se muestra a continuación:

    <website-information info="webContext" />
    

    Razor sabe que el atributo info es una clase, no una cadena, y usted quiere escribir código de C#. Todos los atributos de asistentes de etiquetas que no sean una cadena deben escribirse sin el carácter @.

  5. Ejecute la aplicación y vaya a la vista About para ver la información del sitio web.

    Nota:

    Puede usar el marcado siguiente con una etiqueta de cierre y quitar la línea con TagMode.StartTagAndEndTag del asistente de etiquetas:

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

Asistente de etiquetas de condición

El asistente de etiquetas de condición representa la salida cuando se pasa un valor true.

  1. Agregue la siguiente clase ConditionTagHelper a la carpeta 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. Reemplace el contenido del archivo Views/Home/Index.cshtml con el siguiente marcado:

    @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. Reemplace el método Index del controlador Home por el código siguiente:

    public IActionResult Index(bool approved = false)
    {
        return View(new WebsiteContext
        {
            Approved = approved,
            CopyrightYear = 2015,
            Version = new Version(1, 3, 3, 7),
            TagsToShow = 20
        });
    }
    
  4. Ejecute la aplicación y vaya a la página principal. El marcado del elemento condicional div no se representará. Anexe la cadena de consulta ?approved=true a la dirección URL (por ejemplo, http://localhost:1235/Home/Index?approved=true). approved se establece en true y se muestra el marcado condicional.

Nota

Use el operador nameof para especificar el atributo de destino en lugar de especificar una cadena, como hizo con el asistente de etiquetas bold:

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

El operador nameof protegerá el código si en algún momento debe refactorizarse (tal vez interese cambiar el nombre a RedCondition).

Evitar conflictos de asistentes de etiquetas

En esta sección, escribirá un par de asistentes de etiquetas de vinculación automática. La primera reemplazará el marcado que contiene una dirección URL que empieza con HTTP por una etiqueta delimitadora HTML que contiene la misma dirección URL (y, por tanto, produce un vínculo a la dirección URL). La segunda hará lo mismo para una dirección URL que empieza con WWW.

Dado que estos dos asistentes están estrechamente relacionados y tal vez las refactorice en el futuro, los guardaremos en el mismo archivo.

  1. Agregue la siguiente clase AutoLinkerHttpTagHelper a la carpeta 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}
        }
    }
    

    Nota

    La clase AutoLinkerHttpTagHelper tiene como destino elementos p y usa Regex para crear el delimitador.

  2. Agregue el marcado siguiente al final del archivo 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. Ejecute la aplicación y compruebe que el asistente de etiquetas representa el delimitador correctamente.

  4. Actualice la clase AutoLinker para que incluya la aplicación auxiliar de etiquetas AutoLinkerWwwTagHelper que convertirá el texto www en una etiqueta delimitadora que también contenga el texto www original. El código actualizado aparece resaltado a continuación:

        [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. Ejecutar la aplicación. Observe que el texto www se representa como un vínculo, a diferencia del texto HTTP. Si coloca un punto de interrupción en ambas clases, verá que la clase del asistente de etiquetas HTTP se ejecuta primero. El problema es que la salida del asistente de etiquetas se almacena en caché y, cuando se ejecuta el asistente de etiquetas WWW, sobrescribe la salida almacenada en caché desdel asistente de etiquetas HTTP. Más adelante en el tutorial veremos cómo se controla el orden en el que se ejecutan los asistentes de etiquetas. Corregiremos el código con lo siguiente:

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

    Nota:

    La primera vez que editó los asistentes de etiquetas de vinculación automática, obtuvo el contenido del destino con el código siguiente:

    var childContent = await output.GetChildContentAsync();
    

    Es decir, ha llamado a GetChildContentAsync mediante la salida TagHelperOutput pasada al método ProcessAsync. Como ya se ha indicado, como la salida se almacena en caché, prevalece el último asistente de etiquetas que se ejecuta. Para corregir el error, ha usado el código siguiente:

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

    El código anterior comprueba si se ha modificado el contenido y, en caso afirmativo, obtiene el contenido del búfer de salida.

  6. Ejecute la aplicación y compruebe que los dos vínculos funcionan según lo previsto. Aunque podría parecer que nuestro asistente de etiquetas de vinculación automática es correcta y está completa, tiene un pequeño problema. Si el asistente de etiquetas WWW se ejecuta en primer lugar, los vínculos www no serán correctos. Actualice el código mediante la adición de la sobrecarga Order para controlar el orden en el que se ejecuta la etiqueta. La propiedad Order determina el orden de ejecución en relación con los demás asistentes de etiquetas que tienen como destino el mismo elemento. El valor de orden predeterminado es cero, y se ejecutan en primer lugar las instancias con los valores más bajos.

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

    El código anterior garantizará que el asistente de etiquetas HTTP se ejecuta antes que el asistente de etiquetas WWW. Cambie Order a MaxValue y compruebe que el marcado generado para la etiqueta WWW es incorrecto.

Inspeccionar y recuperar contenido secundario

Los asistentes de etiquetas proporcionan varias propiedades para recuperar contenido.

  • El resultado de GetChildContentAsync se pueden anexar a output.Content.
  • Puede inspeccionar el resultado de GetChildContentAsync con GetContent.
  • Si modifica output.Content, el cuerpo de TagHelper no se ejecutará ni representará a menos que llame a GetChildContentAsync, como en nuestro ejemplo de vinculación automática:
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}
    }
}
  • Varias llamadas a GetChildContentAsync devuelven el mismo valor y no vuelven a ejecutar el cuerpo de TagHelper a menos que se pase un parámetro false que indique que no se use el resultado almacenado en caché.

Carga de la vista parcial minimizada TagHelper

En entornos de producción, se puede mejorar el rendimiento cargando vistas parciales minimizadas. Para aprovechar las ventajas de la vista parcial minimizada en producción, siga estos pasos:

  • Cree o configure un proceso de compilación anterior que minimice las vistas parciales.
  • Utilice el siguiente código para cargar las vistas parciales minimizadas en entornos que no son de desarrollo.
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;
        }
    }