ASP.NET 1.1 Web 应用程序编译和预编译 - 增强版






4.17/5 (4投票s)
2005 年 9 月 5 日
9分钟阅读

82111

780
增强了 C# 或 VB ASP.NET 1.1 Web 应用程序的 .aspx 和 .ascx 文件的预编译或编译方式;包含完整的源代码、编译库、运行时库和演示项目。
请先参阅原文: ASP.NET 1.1 Web 应用程序编译和预编译。
引言
由 **Peter Tewkesbury** 提供的想法
更改 IIS 配置以支持嵌入式资源的这个需求让我很头疼。我负责托管的多个网站,我无法访问 IIS 设置。在我的网站上,有一个处理程序可以从完整图像创建缩略图。代码本身不重要。
这是我的想法
- 添加一个新的处理程序,名为 XYZ。
- 当 XYZ 处理程序接收一个参数(资源名称)时,它将获取并返回该文件。
- 当您预编译源代码时,您可以扫描代码中访问这些文件的对象(Image 对象、带有 src 标签的 HTML 代码等),然后修补代码,以便
- 将资源包含在程序集中,
- 并且编译后的代码被修补,以便调用 XYZ 处理程序并传递正确的参数,从而返回正确的资源文件。
这样您就不需要更改任何 IIS 设置了——这对所有托管网站的用户来说都是一个巨大的优势。
本文关注于实现这一想法及其在应用程序中的使用。源代码也经过重写,设计更优,因此您可以替换旧文章中的版本。
想法
如前一篇文章所述,可以将应用程序所需的任何外部文件(例如 * .gif*、*.css* 等)嵌入到程序集中,并且应用程序将负责处理这些文件的请求,而不是由 IIS 处理。为了让 IIS 将这些文件的请求(例如“*/MyApp/Css/Global.css*”)转发给 ASP.NET 运行时(以及您的应用程序),文件扩展名需要在 IIS 中映射到 *aspnet_isapi.dll*。此外,这些扩展名应映射到 `System.Web.IHttpHander` 实现者(例如 `WebApp.Web.Compilation.Runtime.ResourceFileHandler`),它将从程序集中加载文件内容并将其作为响应返回。此映射通过 *web.config* 进行。例如,如果您希望您的应用程序处理 *.css* 和 *.gif* 文件的请求,您需要手动(或使用脚本)将这些扩展名映射到 IIS 中的 *aspnet_isapi.dll*,并在您的 *web.config* 中添加以下行:
<system.web>
<httpHandlers>
<add verb="*" path="*.css"
type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
WebApp.Web.Compilation.Runtime" />
<add verb="*" path="*.gif"
type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
WebApp.Web.Compilation.Runtime" />
</httpHandlers>
</system.web>
然而,更改 IIS 设置会使应用程序的部署更加困难,而且可能并不总是能够做到。解决方案是让对这些文件的请求看起来像是对扩展名已在 IIS 中注册的文件(例如 *.aspx* 或 *.asmx*)的请求。这可以通过替换这些文件的链接(例如“*/MyApp/Css/Global.css*”)为“handler?p=file_id”类型的链接(例如“*FH.aspx?p=Css/Global.css*”)来完成。“handler”应具有一个扩展名已映射到 IIS 中的 *aspnet_isapi.dll* 的路径(*.aspx*、*.asmx* 等),并且 `file_id` 应为从中可以在程序集中加载相应文件的 Win32 资源的字符串。因此,无需更改任何 IIS 设置,但您的 *web.config* 中应包含以下行:
<system.web>
<httpHandlers>
<add verb="*" path="FH.aspx"
type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
WebApp.Web.Compilation.Runtime" />
</httpHandlers>
</system.web>
实现
解决方案应针对以下问题:
- 嵌入文件
- 链接替换
- 处理嵌入文件的请求。
嵌入文件
将文件嵌入到程序集由 `WebApp.Web.Compilation.ResourceBuilder` 类型完成。它能够构建要包含在编译中的 Win32 资源(*.res*)文件。该类型提供两种方法:
- 通过指定 Win32 资源类型和名称(无符号短整型)添加一个未命名的数据或文件。
- 通过指定 Win32 资源类型和名称(无符号短整型)添加一个已命名的数据或文件。
如果一个资源被命名,则稍后只能通过 `WebApp.Web.Compilation.Runtime.ResourceManager` 类型按其名称访问。资源名称是 `System.String` 类型,与 Win32 资源名称(`System.UInt16` 类型)不同。为了支持资源的字符串名称,会构建一个通用索引并将其保存为附加资源。**注意:** 尽管 Win32 资源名称和/或类型可以是字符串,但此功能尚未实现。
主要问题是如何为要添加的文件选择资源名称。
- 如果所有指向该文件的链接都将被替换为“handler?p=file_id”类型的链接,则名称可以是任何唯一的命名资源字符串。之后,“file_id”的值应为该名称,以便在运行时请求时加载该资源。唯一的问题是名称应以某种方式表示文件的扩展名,以便可以返回适当的 Content-Type HTTP 响应。
- 如果指向该文件的链接保持不变,则资源名称应从文件的虚拟路径派生,以便在运行时可以找到该资源。
此解决方案提供了两个 `WebApp.Web.Compilation.Runtime.IResourceNameProvider` 实现,它们负责为要添加为资源的文件提供名称。
WebApp.Web.Compilation.Runtime.RelativePathNameProvider
WebApp.Web.Compilation.Runtime.EncodedNameProvider
`RelativePathNameProvider` 从文件的相对路径(基于应用程序的根目录)派生资源的名称。也就是说,如果应用程序位于 *C:\MyApp*,文件是 *C:\MyApp\Css\Global.css*,则名称将是 *Css/Global.css*。
`EncodedNameProvider` 与 `RelativePathNameProvider` 执行相同的操作,但对相对路径应用 base64 编码。
链接替换
以下文件可能包含指向其他嵌入文件的链接,应成为目标:
- 页面和用户控件:*.aspx* 和 *.acsx*。
- 资源文件:*.htm*、*.html*、*.js*、*.css* 等。
使用以下策略:
- 仅替换指向嵌入文件的链接:即,指向已指定作为资源嵌入且位于应用程序范围内的文件的链接(即,文件物理上位于应用程序基目录的子目录树内)。**注意:** 要确定一个文件是否位于应用程序范围内,会通过 `System.Web.HttpRequest.MapPath(string,string,bool)` 方法获取物理路径。
- 链接是任何以“'”、“""”、“( )”开头和结尾的字符串,并且包含以目标文件名扩展名之一结尾的路径(确切使用的正则表达式是 ``@"('|"|"|\()(?<1>(\w|.% \/~])*?\.(htm|html|js|...))('|"|"|\))"``)。这允许无论标签和属性的名称如何(如在 JavaScript/CSS 代码中指定的),都可以找到链接。有关更多信息,请参阅 `WebApp.Web.Compilation.FileReferencesFixer` 类型。
页面和用户控件
这些文件中的链接将在文件被 `System.Web.UI.TemplateParser` 后代解析之前进行替换(即,读取文件的内容,修改后传递给解析器)。由于搜索链接的方式,标签内指定的链接(这些标签具有 `runat="server"` 属性)也会被处理(例如 `
**注意:** 仅替换 *.aspx* 和 *.asxc* 文件中的链接。代码隐藏文件中的任何链接都应通过重新设计代码手动处理。
资源文件
这些文件中的链接将在其内容添加到 Win32 资源文件之前进行替换(即,读取文件,修改后追加到资源文件)。这意味着只处理要嵌入的文件本身。未指定作为资源嵌入的文件中的链接将不会被修复。
选择处理程序名称
通常,有两种类型的处理程序名称可供选择:相对于当前虚拟路径(例如,“*FH.aspx*”)和相对于网站根目录(例如,“*/FH.aspx*”、“*/MyApp/FH.aspx*”)。区别在于:
- **浏览器将如何看到链接**:如果您将“*FH.aspx*”指定为处理程序名称,所有链接将显示为“FH.aspx?p= ...”,这相对于包含链接的文件的虚拟路径。这意味着如果您有 *Default.aspx* 包含 ``,并且有 */SubDir/WebForm1.aspx* 包含 ``,浏览器将看到指向两个不同文件的链接:“*/FH.aspx?p= Css/Global.css*”和“*/SubDir/FH.aspx?p= Css/Global.css*”,并会请求两次“*Css/Global.css*”(请注意,“*Css/Global.css*”不是到文件的相对路径,而是资源的名称)。因此,位于子目录中的文件的链接会通过在处理程序名称前添加一定数量的“../”字符串来调整;例如,*/SubDir/WebForm1.aspx* 中的链接将变成“*../FH.aspx?p=Css/Global.css*”。这将导致浏览器只请求一次“*Css/Global.css*”。否则,如果您指定“/FH.aspx?p=...”或“/MyApp/FH.aspx?p=...”作为处理程序名称,则指向同一文件的所有链接将相同(例如,“*/MyApp/FH.aspx?p=Css/Global.css*”)。
- **部署**:如果您选择相对于虚拟路径的处理程序名称(例如,“*FH.aspx*”),应用程序可以部署在任何虚拟目录中,因为链接不包含任何依赖于路径的信息。如果您选择相对于网站根目录的名称,应用程序应部署在特定的虚拟目录中。
如果您需要在代码隐藏文件中生成链接,可以使用 `System.Web.UI.Control.ResolveUrl` 方法(例如 `ResolveUrl("~/FH.aspx?p=..." )`)。
处理嵌入文件的请求
在运行时,请求由 `WebApp.Web.Compilation.Runtime.ResourceFileHandler` 类型处理。您只需要在 *web.config* 中将处理程序名称映射到此类型。
<system.web>
<httpHandlers>
<add verb="*" path="FH.aspx"
type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
WebApp.Web.Compilation.Runtime" />
</httpHandlers>
</system.web>
请注意,`path` 属性的值不包含目录信息(例如,“*/FH.aspx*”、“*/MyApp/FH.aspx*”)。这将导致 ASP.NET 运行时将应用程序内任何位置对“*FH.aspx*”发出的所有请求(例如,“*/FH.aspx*”、“*/MyApp/FH.aspx*”、“*/MyApp/SubDir/FH.aspx*”)转发到 `WebApp.Web.Compilation.Runtime.ResourceFileHandler` 类型。
此外,您还需要在 `WebApp.Web.Compilation.Runtime.ResourceFileManager` 配置部分中指定资源名称提供程序(`WebApp.Web.Compilation.Runtime.IResourceNameProvider` 后代)的类型。
<WebApp.Web.Compilation.Runtime.ResourceFileManager>
<add key="ResourceNameProvider"
value="WebApp.Web.Compilation.Runtime.RelativePathNameProvider"/>
</WebApp.Web.Compilation.Runtime.ResourceFileManager>
或
<WebApp.Web.Compilation.Runtime.ResourceFileManager>
<add key="ResourceNameProvider"
value="WebApp.Web.Compilation.Runtime.EncodedNameProvider" />
</WebApp.Web.Compilation.Runtime.ResourceFileManager>
最后说明
- 与前一个版本的库相比,配置部分有所更改。请参阅源代码和演示。
- 使用前一个版本编译的应用程序可能与此版本不兼容。
Bug
- 如有任何 bug,请报告。