母版页中的 URL (C#)

作者 :Scott Mitchell

下载 PDF

解决母版页中的 URL 如何由于母版页文件与内容页位于不同的相对目录中而中断。 查看通过 ~ 在声明性语法中以编程方式使用 ResolveUrl 和 ResolveClientUrl 来重定 URL。 (另请参阅

简介

在到目前为止我们所看到的所有示例中,主页面和内容页都位于网站的根文件夹) (同一文件夹中。 但是,没有理由母版页和内容页必须位于同一文件夹中。 当然,可以在子文件夹中创建内容页。 同样,你可以创建一个 ~/MasterPages/ 文件夹,用于放置网站的母版页。

将母版和内容页放置在不同文件夹中的一个潜在问题涉及 URL 损坏。 如果母版页包含超链接、图像或其他元素中的相对 URL,则该链接对于驻留在不同文件夹中的内容页无效。 在本教程中,我们将了解此问题的来源以及解决方法。

相对 URL 的问题

如果网页上的 URL 指向的资源位置相对于网站文件夹结构中网页的位置,则称其为 相对 URL 。 任何不以前导正斜杠 (/) 或协议 ((如) ) http:// 开头的 URL 都是相对的,因为它由浏览器根据包含 URL 的网页的位置进行解析。

例如,我们网站有一个 ~/Images/ 包含单个图像文件 PoweredByASPNET.gif的文件夹。 母版页文件Site.masterfooterContent区域中有一个<img>具有以下标记的元素:

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

元素 src 中的 <img> 属性值是相对 URL,因为它不以 /http://开头。 简言之, src 属性值指示浏览器在 Images 子文件夹中查找名为 PoweredByASPNET.gif的文件。

访问内容页面时,上述标记将直接发送到浏览器。 花点时间访问 About.aspx 并查看发送到浏览器的 HTML 源。 你会发现母版页中完全相同的标记已发送到浏览器。

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

如果内容页位于根文件夹中, About.aspx () 所有内容都按预期方式工作,因为根文件夹有一个 Images 子文件夹。 但是,如果内容页位于与母版页不同的文件夹中,则情况会中断。 为了说明这一点,请创建一个名为 的 Admin子文件夹。 接下来,将名为 Default.aspx 的内容页添加到 Admin 文件夹,确保将新页面绑定到 Site.master 母版页。

注意

母版页中指定标题、元标记和其他 HTML 标头 教程中,我们创建了一个名为 BasePage 的自定义基页类,如果未显式分配内容页的标题 () ,该类会自动设置内容页的标题。 不要忘记让新创建页的代码隐藏类派生自 BasePage ,以便它可以利用此功能。

创建此内容页后,解决方案资源管理器应类似于图 1。

已将新文件夹和 ASP.NET 页添加到项目

图 01:已向项目添加新文件夹和 ASP.NET 页

接下来,更新 Web.sitemap 文件以包含本课程的新 <siteMapNode> 条目。 以下 XML 显示完整的 Web.sitemap 标记,现在包括添加第三 <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>

新创建 Default.aspx 的页面应有四个 Content 控件,对应于 中的 Site.master四个 ContentPlaceHolders。 将一些文本添加到引用 MainContent ContentPlaceHolder 的 Content 控件,然后通过浏览器访问页面。 如图 2 所示,浏览器找不到 PoweredByASPNET.gif 图像文件。 这是怎么回事?

~/Admin/Default.aspx区域About.aspx发送内容页面的 HTML footerContent 与页面相同:

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

<img>由于 元素的 src 属性是相对 URL,因此浏览器会尝试查找Images相对于网页的文件夹位置的文件夹。 换句话说,浏览器正在查找图像文件 Admin/Images/PoweredByASPNET.gif

找不到 PoweredByASPNET.gif 映像文件

图 02PoweredByASPNET.gif 单击查看 全尺寸图像 (找不到图像文件)

将相对 URL 替换为绝对 URL

相对 URL 的相反是 绝对 URL,该 URL 以正斜杠 (/) 或协议(如 ) http://开头。 由于绝对 URL 指定已知固定点中的资源位置,因此,无论网页在网站文件夹结构中的位置如何,同一绝对 URL 在任何网页中都有效。

若要修复图 2 中所示的损坏图像,我们需要更新 <img> 元素的 src 属性,使其使用绝对 URL 而不是相对 URL。 若要确定正确的绝对 URL,请访问网站中的一个网页并检查地址栏。 如图 2 中的地址栏所示,Web 应用程序的完全限定路径为 http://localhost:3908/ASPNET_MasterPages_Tutorial_04_CS/。 因此,我们可以将 <img> 元素的 src 属性更新为以下两个绝对 URL 之一:

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

请花点时间使用 <img> 上述窗体之一将元素的属性 src 更新为绝对 URL,然后通过浏览器访问页面 ~/Admin/Default.aspx 。 这一次,浏览器将正确查找并显示 PoweredByASPNET.gif 图像文件, (见图 3) 。

现在显示 PoweredByASPNET.gif 图像

图 03:现在 PoweredByASPNET.gif 显示图像 (单击以查看全尺寸图像)

虽然绝对 URL 中的硬编码有效,但它会将 HTML 与网站的服务器和文件夹位置紧密耦合,这可能会更改。 使用表单 http://localhost:3908/... 的绝对 URL 是脆弱的,因为每次启动 Visual Studio 的内置 ASP.NET 开发 Web 服务器时,都会自动选择前面的 localhost 端口号。 同样, http://localhost 部件仅在本地测试时有效。 将代码部署到生产服务器后,URL 基将更改为其他内容,例如 http://www.yourserver.com。 表单 /ASPNET_MasterPages_Tutorial_04_CS/... 中的绝对 URL 也存在相同的脆性,因为通常此应用程序路径在开发和生产服务器之间有所不同。

好消息是,ASP.NET 提供了一种在运行时生成有效相对 URL 的方法。

使用~ResolveClientUrl

ASP.NET 允许页面开发人员使用平铺 () ~ 来指示 Web 应用程序的根,而不是硬编码绝对 URL。 例如,在本教程前面,我使用文本中的表示法~/Admin/Default.aspx来引用 文件夹中的页面Default.aspxAdmin~指示文件夹Admin是 Web 应用程序的根目录的子文件夹。

ControlResolveClientUrl 方法采用 URL,并将其修改为适用于控件所在的网页的相对 URL。 例如,从 About.aspx 调用 ResolveClientUrl("~/Images/PoweredByASPNET.gif")Images/PoweredByASPNET.gif返回 。 但是,从 ~/Admin/Default.aspx调用它将返回 ../Images/PoweredByASPNET.gif

注意

由于所有 ASP.NET 服务器控件都派生自 Control 类,因此所有服务器控件都有权 ResolveClientUrl 访问 方法。 甚至 类 Page 派生自 Control 类,这意味着可以直接从 ASP.NET 页的代码隐藏类使用此方法。

~在声明性标记中使用

多个 ASP.NET Web 控件包括与 URL 相关的属性:HyperLink 控件具有 NavigateUrl 属性;图像控件具有 ImageUrl 属性,等等。 呈现时,这些控件将其 URL 相关的属性值传递给 ResolveClientUrl。 因此,如果这些属性包含 ~ 用于指示 Web 应用程序的根的 ,则会将 URL 修改为有效的相对 URL。

请记住,只有 ASP.NET 服务器控件在其 URL 相关属性中转换 ~ 。 如果 出现在 ~ 静态 HTML 标记中,例如 <img src="~/Images/PoweredByASPNET.gif" />,ASP.NET 引擎会将 连同 HTML 内容的其余部分一起发送到 ~ 浏览器。 浏览器假定 是 ~ URL 的一部分。 例如,如果浏览器收到标记<img src="~/Images/PoweredByASPNET.gif" />,则它假定有一个名为 的~子文件夹,其中包含包含图像文件的PoweredByASPNET.gif子文件夹Images

若要修复 中的 Site.master图像标记,请将现有 <img> 元素替换为 ASP.NET 图像 Web 控件。 将图像 Web 控件设置为 IDPoweredByImage,将其 ImageUrl 属性设置为 ~/Images/PoweredByASPNET.gif,将其 AlternateText 属性设置为“由 ASP.NET 提供支持!”

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

对母版页进行此更改后,请再次重新访问该 ~/Admin/Default.aspx 页。 这一次, PoweredByASPNET.gif 图像文件显示在页面中, (请参阅图 3) 。 呈现图像 Web 控件时, ResolveClientUrl 它使用 方法来解析其 ImageUrl 属性值。 ImageUrl在 中 ~/Admin/Default.aspx 转换为相应的相对 URL,如以下 HTML 源代码片段所示:

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

注意

除了在基于 URL 的 Web 控件属性中使用外, ~ 还可以在调用 Response.RedirectServer.MapPath 方法等时使用 。 此外, ResolveClientUrl 可以直接从 ASP.NET 或母版页的声明性标记调用 方法。

修复母版页的剩余相对 URL

除了我们刚刚修复的 <img> 中的 footerContent 元素外,母版页还包含一个需要我们注意的相对 URL。 该区域topContent包含指向 的链接“母版页教程”。Default.aspx

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

由于此 URL 是相对的,因此它会将用户发送到 Default.aspx 他们正在访问的内容页文件夹中的页面。 若要使此链接始终指向根文件夹中的 , Default.aspx 我们需要将 元素替换为 <a> HyperLink Web 控件,以便可以使用 ~ 表示法。

<a>删除元素标记并在其位置添加 HyperLink 控件。 将 HyperLink 的 ID 设置为 lnkHome,其 NavigateUrl 属性设置为 ~/Default.aspx,将其 Text 属性设置为“母版页教程”。

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

就这么简单! 此时,无论母版页和内容页位于哪个文件夹中,内容页呈现时,母版页中的所有 URL 都是正确基于的。

节中的<head>自动 URL 解析

“使用母版页创建 Site-Wide 布局”教程中<head>,我们向Styles.css区域中的 文件添加了 <link>

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

<link>虽然元素的属性href是相对的,但它在运行时会自动转换为适当的路径。 正如我们在 母版页中指定标题、元标记和其他 HTML 标头 教程中所述, <head> 该区域实际上是一个服务器端控件,它允许它在呈现时修改其内部控件的内容。

若要验证这一点,请重新访问 ~/Admin/Default.aspx 页面并查看发送到浏览器的 HTML 源。 如以下代码片段所示, <link> 元素的属性 href 已自动修改为相应的相对 URL ../Styles.css

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

总结

母版页通常包含必须通过 URL 指定的链接、图像和其他外部资源。 由于母版页和内容页可能不存在于同一文件夹中,因此请务必避免使用相对 URL。 虽然可以使用硬编码的绝对 URL,但这样做会将绝对 URL 紧密耦合到 Web 应用程序。 如果绝对 URL 发生更改(在移动或部署 Web 应用程序时经常发生),则必须记住返回并更新绝对 URL。

理想的方法是使用平铺 (~) 来指示应用程序根。 ASP.NET 包含 URL 相关属性的 Web 控件在运行时将 映射到 ~ 应用程序根。 在内部,Web 控件使用 Control 类的 ResolveClientUrl 方法来生成有效的相对 URL。 此方法是公共的,可从包括类) 在内的 Page 每个服务器控件 (使用,因此如果需要,可以通过代码隐藏类以编程方式使用它。

编程快乐!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

Scott Mitchell 是多本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 3.5。 可以在 上联系 mitchell@4GuysFromRolla.com 斯科特,也可以通过他的博客在 联系 http://ScottOnWriting.NET

特别感谢

有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处 mitchell@4GuysFromRolla.com放置一行。