Direcciones URL en páginas maestras (C#)

por Scott Mitchell

Descargar PDF

Soluciona el problema de que las URL de la página maestra se rompan debido a que el archivo de la página maestra se encuentre en un directorio relativo distinto al de la página de contenido. Examina el cambio de base de URL mediante ~ en la sintaxis declarativa y el uso de ResolveUrl y ResolveClientUrl mediante programación. (Vea también

Introducción

En todos los ejemplos que hemos visto hasta ahora, las páginas maestras y de contenido se han ubicado en la misma carpeta (la carpeta raíz del sitio web). Pero no hay ninguna razón por la que las páginas maestras y de contenido deben estar en la misma carpeta. Sin duda, puede crear páginas de contenido en subcarpetas. Del mismo modo, puede crear una carpeta ~/MasterPages/ donde coloque las páginas maestras del sitio.

Un posible problema con la colocación de páginas maestras y de contenido en diferentes carpetas implica direcciones URL rotas. Si la página maestra contiene direcciones URL relativas en hipervínculos, imágenes u otros elementos, el vínculo no será válido para las páginas de contenido que residen en una carpeta diferente. En este tutorial se examina el origen de este problema, así como soluciones alternativas.

El problema con direcciones URL relativas

Se dice que una URL de una página web es una URL relativa si la ubicación del recurso al que apunta es relativa a la ubicación de la página web en la estructura de carpetas del sitio web. Cualquier dirección URL que no comience con una barra diagonal inicial (/) o un protocolo (como http://) es relativo porque el explorador lo resuelve en función de la ubicación de la página web que contiene la dirección URL.

Por ejemplo, nuestro sitio web tiene una carpeta ~/Images/ con un único archivo de imagen, PoweredByASPNET.gif. El archivo de página maestra Site.master tiene un elemento <img> en la región de footerContent con el marcado siguiente:

<div id="footerContent">
 <img src="Images/PoweredByASPNET.gif" alt="Powered by ASP.NET!" />
</div>

El valor del atributo src del elemento <img> es una dirección URL relativa porque no empieza por / ni http://. En resumen, el valor del atributo src indica al explorador que busque en la subcarpeta Images para un archivo denominado PoweredByASPNET.gif.

Al visitar una página de contenido, el marcado anterior se envía directamente al explorador. Dedique un momento a visitar About.aspx y vea el origen HTML enviado al explorador. Encontrará que el mismo marcado de la página maestra se envió al explorador.

<div id="footerContent">
 <img src="Images/PoweredByASPNET.gif" alt="Powered by ASP.NET!" />
</div>

Si la página de contenido está en la carpeta raíz (tal como está About.aspx) todo funciona según lo esperado porque hay una subcarpeta Images relativa a la carpeta raíz. Sin embargo, las cosas se desglosan si la página de contenido está en una carpeta diferente de la página maestra. Para ilustrar esto, cree una subcarpeta denominada Admin. A continuación, agregue una página de contenido denominada Default.aspx a la carpeta Admin, asegurándose de enlazar la nueva página a la página maestra de Site.master.

Nota:

En el tutorial Especificar el título, las etiquetas meta y otros encabezados HTML del tutorial de página maestra, creamos una clase de página base personalizada denominada BasePage que establece automáticamente el título de la página de contenido (si no se asignó explícitamente). No olvide tener la clase de código subyacente de la página recién creada derivada de BasePage para que pueda usar esta funcionalidad.

Después de crear esta página de contenido, el Explorador de soluciones debe tener un aspecto similar a la figura 1.

A New Folder and ASP.NET Page Have Been Added to the Project

Figura 01: Se ha agregado una nueva carpeta y página ASP.NET al proyecto

A continuación, actualice el archivo Web.sitemap para incluir una nueva entrada de <siteMapNode> para esta lección. El siguiente XML muestra el marcado completo Web.sitemap, que ahora incluye la adición de un tercer elemento <siteMapNode>.

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
 <siteMapNode url="~/Default.aspx" title="Home">
 <siteMapNode url="~/About.aspx" title="About the Author" />
 <siteMapNode url="~/MultipleContentPlaceHolders.aspx" title="Using Multiple ContentPlaceHolder Controls" />
 <siteMapNode url="~/Admin/Default.aspx" title="Rebasing URLs" />
 </siteMapNode>
</siteMap>

La página Default.aspx recién creada debe tener cuatro controles Content correspondientes a los cuatro ContentPlaceHolders de Site.master. Agregue texto al control Content que hace referencia al ContentPlaceHolder MainContent y luego visite la página a través de un explorador. Como se muestra en la figura 2, el explorador no encuentra el archivo de imagen PoweredByASPNET.gif. ¿Qué ocurre aquí?

A la página de contenido ~/Admin/Default.aspx se le envía el mismo HTML para la región footerContent que a la página About.aspx:

<div id="footerContent">
 <img src="Images/PoweredByASPNET.gif" alt="Powered by ASP.NET!" />
</div>

Dado que el atributo src del elemento <img> es una dirección URL relativa, el explorador intenta buscar una carpeta Images relativa a la ubicación de la carpeta de la página web. En otras palabras, el explorador busca el archivo de imagen Admin/Images/PoweredByASPNET.gif.

The PoweredByASPNET.gif Image File Cannot Be Found

Figura 02: No se encuentra el archivo de imagen de PoweredByASPNET.gif (Haga clic para ver la imagen de tamaño completo)

Reemplazar direcciones URL relativas por direcciones URL absolutas

Lo contrario de una URL relativa es una URL absoluta, que es la que empieza por una barra oblicua (/) o un protocolo como http://. Dado que una URL absoluta especifica la ubicación de un recurso a partir de un punto fijo conocido, la misma URL absoluta es válida en cualquier página web, independientemente de la ubicación de la página web en la estructura de carpetas del sitio web.

Para solucionar la imagen rota que se muestra en la figura 2, es necesario actualizar el atributo src del elemento <img> para que use una dirección URL absoluta en lugar de una relativa. Para determinar la dirección URL absoluta correcta, visite una de las páginas web de su sitio web y examine la barra de direcciones. Como se muestra en la barra de direcciones de la figura 2, la ruta de acceso completa a la aplicación web es http://localhost:3908/ASPNET_MasterPages_Tutorial_04_CS/. Por lo tanto, podríamos actualizar el atributo src del elemento <img> a cualquiera de las dos URL absolutas siguientes:

  • /ASPNET_MasterPages_Tutorial_04_CS/Images/PoweredByASPNET.gif
  • http://localhost:3908/ASPNET_MasterPages_Tutorial_04_CS/Images/PoweredByASPNET.gif

Dedique un momento a actualizar el atributo src del elemento <img> a una dirección URL absoluta mediante uno de los formularios mostrados anteriormente y luego visite la página de ~/Admin/Default.aspx a través de un explorador. Esta vez, el explorador buscará y mostrará correctamente el archivo de imagen PoweredByASPNET.gif (vea la figura 3).

The PoweredByASPNET.gif Image is Now Displayed

Figura 03: La imagen PoweredByASPNET.gif ahora se muestra (Haga clic para ver la imagen de tamaño completo)

Aunque la codificación rígida en la dirección URL absoluta funciona, acopla estrechamente su HTML a la ubicación del servidor y la carpeta del sitio web, lo que puede cambiar. El uso de una dirección URL absoluta del formulario http://localhost:3908/... es frágil porque el número de puerto anterior localhost se selecciona automáticamente cada vez que se inicia el servidor web integrado de Visual Studio ASP.NET Development. Del mismo modo, la parte http://localhost solo es válida cuando se prueba localmente. Una vez implementado el código en un servidor de producción, la base de direcciones URL cambiará a otra cosa, como http://www.yourserver.com. La dirección URL absoluta con el formato /ASPNET_MasterPages_Tutorial_04_CS/... también sufre de la misma fragilidad, ya que a menudo esta ruta de acceso de aplicación difiere entre los servidores de desarrollo y producción.

La buena noticia es que ASP.NET ofrece un método para generar una dirección URL relativa válida en tiempo de ejecución.

Uso~yResolveClientUrl

En lugar de codificar de forma rígida una dirección URL absoluta, ASP.NET permite a los desarrolladores de páginas usar la tilde (~) para indicar la raíz de la aplicación web. Por ejemplo, anteriormente en este tutorial usé el ~/Admin/Default.aspx de notación en el texto para hacer referencia a la página Default.aspx de la carpeta Admin. El ~ indica que la carpeta Admin es una subcarpeta de la raíz de la aplicación web.

El método ResolveClientUrl de la clase Control toma una URL y la modifica a una URL relativa apropiada para la página web en la que reside el control. Por ejemplo, llamar a ResolveClientUrl("~/Images/PoweredByASPNET.gif") desde About.aspx devuelve Images/PoweredByASPNET.gif. Sin embargo, llamarlo desde ~/Admin/Default.aspx devuelve ../Images/PoweredByASPNET.gif.

Nota:

Dado que todos los controles de servidor ASP.NET derivan de la clase Control, todos los controles de servidor tienen acceso al método ResolveClientUrl. Incluso la clase Page deriva de la clase Control, lo que significa que puede usar este método directamente desde las clases de código subyacente de las páginas de ASP.NET.

Uso de~en el marcado declarativo

Varios controles web ASP.NET incluyen propiedades relacionadas con direcciones URL: el control HyperLink tiene una propiedad NavigateUrl; El control Imagen tiene una propiedad ImageUrl; y así sucesivamente. Cuando se representa, estos controles pasan sus valores de propiedad relacionados con la dirección URL a ResolveClientUrl. Por lo tanto, si estas propiedades contienen un ~ para indicar la raíz de la aplicación web, la dirección URL se modificará a una dirección URL relativa válida.

Tenga en cuenta que solo controles de servidor ASP.NET transforman el ~ en sus propiedades relacionadas con direcciones URL. Si un ~ aparece en el marcado HTML estático, como <img src="~/Images/PoweredByASPNET.gif" />, el motor de ASP.NET envía el ~ al explorador junto con el resto del contenido HTML. El explorador supone que el ~ forma parte de la dirección URL. Por ejemplo, si el explorador recibe el marcado <img src="~/Images/PoweredByASPNET.gif" /> supone que hay una subcarpeta denominada ~ con una subcarpeta Images que contiene el archivo de imagen PoweredByASPNET.gif.

Para corregir el marcado de imagen en Site.master, reemplace el elemento <img> existente por un control web de imagen de ASP.NET. Establezca el control Image Web ID en PoweredByImage, su propiedad ImageUrl en ~/Images/PoweredByASPNET.gif, y su propiedad AlternateText en "Con tecnología de ASP.NET"

<div id="footerContent">
 <asp:Image ID="PoweredByImage" runat="server" ImageUrl="~/Images/PoweredByASPNET.gif" 
    AlternateText="Powered by ASP.NET!" />
</div>

Después de realizar este cambio en la página maestra, vuelva a visitar la página ~/Admin/Default.aspx. Esta vez el archivo de imagen PoweredByASPNET.gif aparece en la página (vea la figura 3). Cuando se representa el control Web de imagen, usa el método ResolveClientUrl para resolver su valor de propiedad ImageUrl. En ~/Admin/Default.aspx el ImageUrl se convierte en una dirección URL relativa adecuada, como se muestra en el siguiente fragmento de código fuente HTML:

<div id="footerContent">
 <img id="ctl00_PoweredByImage" src="../Images/PoweredByASPNET.gif" alt="Powered by ASP.NET!" />
</div>

Nota:

Además de usarse en propiedades de control web basadas en direcciones URL, también se puede usar el ~ al llamar a los métodos Response.Redirect y Server.MapPath, entre otros. Además, el método ResolveClientUrl se puede invocar directamente desde un marcado declarativo de ASP.NET o página maestra.

Corrección de las direcciones URL relativas restantes de la página maestra

Además del elemento <img> de la footerContent que acabamos de corregir, la página maestra contiene una dirección URL más relativa que requiere nuestra atención. La región topContent incluye el vínculo "Tutoriales de páginas maestras", que apunta a Default.aspx.

<div id="topContent">
 <a href="Default.aspx">Master Pages Tutorials</a>
</div>

Dado que esta dirección URL es relativa, enviará al usuario a la página Default.aspx en la carpeta de la página de contenido que está visitando. Para que este vínculo apunte siempre a Default.aspx en la carpeta raíz, es necesario reemplazar el elemento <a> por un control web de HyperLink para poder usar la notación ~.

Quite el marcado de elemento <a> y agregue un control HyperLink en su lugar. Establezca el ID de HyperLink en lnkHome, su propiedad NavigateUrl en ~/Default.aspx y su propiedad Text en "Tutoriales de páginas maestras".

<div id="topContent">
 <asp:HyperLink ID="lnkHome" runat="server" NavigateUrl="~/Default.aspx"
    Text="Master Pages Tutorials" />
</div>

Eso es todo. En este punto, todas las direcciones URL de nuestra página maestra se basan correctamente cuando se representan en una página de contenido, independientemente de las carpetas en las que se encuentran la página maestra y la página de contenido.

Resolución automática de direcciones URL en la sección<head>

En el tutorial Creación de un diseño de todo el sitio mediante páginas maestras hemos agregado un <link> al archivo Styles.css en la región de <head>:

<head runat="server">
 <title>Untitled Page</title>
 <asp:ContentPlaceHolder id="head" runat="server">
 </asp:ContentPlaceHolder>
 <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>

Aunque el atributo href del elemento <link> es relativo, se convierte automáticamente en una ruta de acceso adecuada en tiempo de ejecución. Como hemos explicado en el tutorial de Especificar el título, las etiquetas meta y otros encabezados HTML del tutorial de página maestra, la región de <head> es realmente un control del lado servidor, que permite modificar el contenido de sus controles internos cuando se representa.

Para comprobarlo, vuelva a consultar la página ~/Admin/Default.aspx y vea el origen HTML enviado al explorador. Como se muestra en el siguiente fragmento de código, el atributo href del elemento <link> se ha modificado automáticamente a una dirección URL relativa adecuada, ../Styles.css.

<head>
 <title>
 Default
 </title>
 <link href="../Styles.css" rel="stylesheet" type="text/css" />
</head>

Resumen

Las páginas maestras suelen incluir vínculos, imágenes y otros recursos externos que se deben especificar a través de una dirección URL. Dado que es posible que la página maestra y las páginas de contenido no existan en la misma carpeta, es importante abstenerse de usar direcciones URL relativas. Aunque es posible utilizar direcciones URL absolutas codificadas, al hacerlo se acopla estrechamente la dirección URL absoluta a la aplicación web. Si cambia la dirección URL absoluta ( como suele hacer al mover o implementar una aplicación web), tendrá que recordar volver atrás y actualizar las direcciones URL absolutas.

El enfoque ideal es usar la tilde (~) para indicar la raíz de la aplicación. Los controles web de ASP.NET que contienen propiedades relacionadas con direcciones URL asignan el ~ a la raíz de la aplicación en tiempo de ejecución. Internamente, los controles web usan el método ResolveClientUrl de la clase Control para generar una dirección URL relativa válida. Este método es público y está disponible en todos los controles de servidor (incluida la clase Page), por lo que puede usarlo mediante programación desde las clases de código subyacente, si es necesario.

¡Feliz programación!

Lecturas adicionales

Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:

Acerca del autor

Scott Mitchell, autor de varios libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 3.5 in 24 Hours. Se puede contactar con Scott en mitchell@4GuysFromRolla.com o a través de su blog en http://ScottOnWriting.NET.

Agradecimientos especiales a

¿Le interesa revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.