作者 :Scott Mitchell
下载 PDF
解决母版页中的 URL 如何由于母版页文件与内容页位于不同的相对目录中而中断。 查看通过 ~ 在声明性语法中以编程方式使用 ResolveUrl 和 ResolveClientUrl 来重定 URL。 (另请参阅
简介
在到目前为止我们所看到的所有示例中,主页面和内容页都位于网站的根文件夹) (同一文件夹中。 但是,没有理由母版页和内容页必须位于同一文件夹中。 当然,可以在子文件夹中创建内容页。 同样,你可以创建一个 ~/MasterPages/
文件夹,用于放置网站的母版页。
将母版和内容页放置在不同文件夹中的一个潜在问题涉及 URL 损坏。 如果母版页包含超链接、图像或其他元素中的相对 URL,则该链接对于驻留在不同文件夹中的内容页无效。 在本教程中,我们将了解此问题的来源以及解决方法。
相对 URL 的问题
如果网页上的 URL 指向的资源位置相对于网站文件夹结构中网页的位置,则称其为 相对 URL 。 任何不以前导正斜杠 (/
) 或协议 ((如) ) http://
开头的 URL 都是相对的,因为它由浏览器根据包含 URL 的网页的位置进行解析。
例如,我们网站有一个 ~/Images/
包含单个图像文件 PoweredByASPNET.gif
的文件夹。 母版页文件Site.master
在footerContent
区域中有一个<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。

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

图 02: PoweredByASPNET.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) 。

图 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.aspx
Admin
。 ~
指示文件夹Admin
是 Web 应用程序的根目录的子文件夹。
类Control
的ResolveClientUrl
方法采用 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 控件设置为 ID
PoweredByImage
,将其 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.Redirect
和 Server.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放置一行。