母版页中的 URL (VB)
解决由于母版页文件与内容页位于不同的相对目录中而导致母版页中的 URL 如何中断。 查看通过 ~ 在声明性语法中重新设置 URL 的基数,并通过编程方式使用 ResolveUrl 和 ResolveClientUrl。 (另请参阅
简介
到目前为止,在我们所看到的所有示例中,母版页和内容页都位于网站根文件夹) (同一文件夹中。 但是,没有理由母版页和内容页必须位于同一文件夹中。 当然,可以在子文件夹中创建内容页。 同样,可以创建一个 ~/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
的页面应具有四个与 中的 Site.master
四个 ContentPlaceHolder 对应的内容控件。 将一些文本添加到引用 ContentPlaceHolder 的 MainContent
Content 控件,然后通过浏览器访问页面。 如图 2 所示,浏览器找不到 PoweredByASPNET.gif
图像文件。 这是怎么回事?
向~/Admin/Default.aspx
区域发送的内容页面的 HTML 与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_VB/
。 因此,我们可以将 <img>
元素的 src
属性更新为以下两个绝对 URL 之一:
/ASPNET_MasterPages_Tutorial_04_VB/Images/PoweredByASPNET.gif
http://localhost:3908/ASPNET_MasterPages_Tutorial_04_VB/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 Development Web 服务器时,都会自动选择 localhost 前面的端口号。 同样, http://localhost
该部分仅在本地测试时有效。 将代码部署到生产服务器后,URL 基将更改为其他内容,例如 http://www.yourserver.com
。 表单 /ASPNET_MasterPages_Tutorial_04_VB/...
中的绝对 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 布局 教程中, <link>
我们向 Styles.css
区域中的 文件 <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>
<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 担任独立顾问、培训师和作家。 他的最新书是 山姆自学 ASP.NET 在24小时内3.5。 可在 上或通过他的博客http://ScottOnWriting.NET联系 mitchell@4GuysFromRolla.com Scott。
特别感谢
有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处放置一行 mitchell@4GuysFromRolla.com。