在开发时,如果我们正在使用 VS + IIS 的模式进行开发与调试,或者当我们在使用 VS 内置的开发人员服务器组件调试 Asp.net 应用程序时,我们从正在浏览的页面偶尔收到一个消息:应用程序已脱机。这是由于VS正在更新网站,为了给正在更新的网站提供一个保护,调试工具在网站根目录放置了 App_Offline.htm 以防止在不正确的时机访问正在被更新的网站。一般地,当 VS 完成更新网站(生成操作或基他操作)之后会自动将这个文件删除。

本文将详细地讨论此文件,及如何将应用程序定义为脱机,并创建我们自己自定义的脱机机制。

app_offline.htm 在 ASP.NET 中是一个有用的文件。当我们对服务器正在进行维护,需要暂时关闭网站时,可以用到它,它向客户端提示服务器正在维护。当 asp.net 应用程序根目录下存在这个 App_Offline.htm 时,一旦有客户端试图访问这个 ASP.NET 网站,服务器就会向其传送 app_offline.htm 的内容。基于用户体验上来说,它是十分有意义的。

关于此文件,MSDN没有专门的文档,只有几篇文章中提到这样一句话:“如果存在 App_offline.htm 文件,对网站的任何请求都将重定向到该文件,它显示一条友好消息,通知客户端网站正在进行更新。” 事实上,系统自动生成的这个 App_Offline.htm 文件是十分丑陋的,因此我们想到要自己定义一个这样的 App_Offline.htm 文件(可以直接修改系统自动生成的那个文件),或者自己定义自己的一个脱机机制。

首先,关于 App_Offline.htm,到目前为止,不得不澄清几个问题:

1 文件的大小可以认为是不受限的,可是很小也可以是很大。很多人说文件大小应该超过 512 Byte,而又有另一个系列的文章认为此文件的大小是无关紧要的。我必须再一次对此问题进行澄清一下:事实上, Internet Explorer 8.0、Opera 10.50、Firefox 3.5 中,可以认为这个文件的大小已被忽略了,浏览器可以接受它以任意大小的形式出现;但在 Chrome 4.1 中,如果此文件的大小低于 512 Byte,它将被忽略,Chrome 将给用户呈现一个“链接已被破坏”的消息。通过跟踪 http 请求可以知道,客户端在接收 App_Offline.htm 时,接收了一个 404 的状态码。这就决定了它的内容如果过少,很有可能被一些浏览器抛弃——这正是一些浏览器将它解析为”该页无法显示“的本质原因所在。

2 文件名称:大小写不敏感,扩展名必须是 htm。在 Windows 系统中文件名称是大小写不敏感的,包括 Asp.net 页面名称、QuestString、各种请求的资源名称等都是不分大小写的,因此 App_Offline.htm 的文件名是不分大小写的;注意 “app_offiline.htm” 作为一个文件的名字,在 URL 中它是一个客户端请求的资源的名称,它是既定的,也是唯一的,而且系统仅内置它作为应用程序脱机的标准文件名,而扩展名作为整个文件或资源的名称不可分割的一部分,更改了它,自然整个文件的名字就变了。所以其扩展名不能是除 htm 以外的任何其他扩展名。

3 此文件仅影响当前应用程序。在 Asp.net 中,一个“网站”被当作一个“应用程序”对待,我们暂且把它称之为一个“Asp.Net网站”,这与IIS的“网站”的概念略有不同:一个IIS中的网站可以以虚拟目录的形式包含若干个“Asp.Net网站”。也就是说在IIS中,一个应用程序可以被部署在一个独立网站中,也可以被部署在某个网站的一个虚拟目录中。事实上,不用管当前的应用程序是如何部署的,记住一点,App_Offline.htm 文件影响着当前的应用程序,即Asp.Net网站;而不会对当前IIS网站中其他虚拟目录造成任何影响;即使当前网站已经含有虚拟目录(无论这虚拟目录的物理路径是否真的位于当前网站网站的目录下),这些虚拟目录也有其他应用程序,这些部署在虚拟目录里的应用程序仍将不受影响。

4 此文件影响所有受当前应用程序 asp.net  ISAPI 托管的文件类型。此文件影响所有受 ASP.NET 引擎托管的的文件,例如我们常见的 .aspx,.ascx等,也有我们不常见的 .axd 等。诸如 .html、图片等静态文件,在通常情况下是不由 asp.net 引擎托管的,因此他们也就不会受此文件影响了。不用担心,如果你在 IIS 中做过自定义映射,那么这些经过映射过的类型也将受此影响,也就是说无需为了让这些类型受它影响而专门为它们做映射。我想聪明的你已经猜出原因了,是的,这是因为 app_offline.htm 的作用机制完全来自 asp.net 引擎,而不是 IIS 本身。

自定义应用程序的脱机机制

虽然上述使用 App_Offline.htm 文件能解决一大部分问题,但我不得不提到他的几个缺点:

1 有目共睹的,它的样式实在是很丑陋;

2 它的提示仍然不符合我们日常在做应用开发时给用户的提示需求——它只做了一个极为常规的提示;

3 如上面”澄清“部分所述,它只有拥有一个正确的大小才能被正确被展示出来;

4 它是系统生成的,也很有可能被系统在某一个时刻删去,不易受操作人员控制;

5 以上做法并不利于搜索引擎的抓取。如果网站根目录被放有 App_Offline.htm,用户将得到一个 404 错误码。当搜索引擎在试图访问某一页面时得到一个 404 错误码,那么搜索引擎将通知其数据库将正在抓取的页面的所有信息删除。虽然我知道这个文件存在的时间不会太久,但你无法保证搜索引擎不会在此时到来。我想这并不是网站管理人员所期待的。

鉴于以上原因,我们想制作一个用户用好、具有更多有用信息、不返回404错误码、而且是操作人员能主动控制的应用程序脱机机制。

我们可以在 Web.Config 的 appSettings 节创建一个名称为”ApplicationIsOffline“ 的键(如果您的项目有自己的配置提供者,也可以在您自己的配置提供器中创建对应项),以指示应用程序是否脱机,然后以编程的方式来检测此值,并在此配置值指定脱机时做出相应处理。

我们可以采用 HttpModule 的方式来处理所有正在指向当前网站的请求。如果上述配置键指示当前应用程序已脱机,则我们向客户端传送(使用  Server.Transfer() 方法)一个我们之前就制作好的错误页面。写好此 HttpModule 后,在 Web.Config 中做相关配置,将所有请求均映射到此 HttpModule 上即可实现本文中提到的功能。下面是此 HttpModule 的 .cs 相关代码,如果需要更多关于此示例的代码,请在文末下载完整的示例。

HttpContext context = ((HttpApplication)sender).Context;

if (ConfigurationManager.AppSettings[“ApplicationIsOffline”].ToLower() == “true”)

{

string extension = Path.GetExtension(context.Request.Path);

   // 我们可以自己定义我们需要对此规则应用的文件的扩展名

   string targetedExtensions = ”.aspx.ashx.asmx”;

   if (targetedExtensions.IndexOf(extension, StringComparison.OrdinalIgnoreCase) == -1)

      return;

   context.Server.Transfer(“~/ciznxcustomedofflinefile.html”);

}

可以预料,通过上文所述的方法向客户端的输出是正常的输出一个 ciznxcustomedofflinefile.html 文件,因此客户端会得到一个 200 的状态码,这正是我们所期待的。

在这里下载本文相关示例:ChekOffline.rar (2.32 kb)