Sometimes it’s necessary to take an ASP.NET application offline. A common scenario is when you need to update the database or running some mainteanance updates that might put the application or database into an unstable state and you generally don’t want to have your customers/users accessing the site while the application is in this unstable state.
There are a variety of ways to accomplish this of course. First you can of course physically pull the plug on your Web server <g>. That’ll keep ‘em out alright, but it’s a pretty brute force approach obviously and not really convenient if your server is not easily accessible. Along the same lines you can also change your IIS configuration settings to some other port to temporarily change your application’s access port, so common users are locked out and you can still access the server if you need to for any administrative purposes. Also pretty brutal.
A higher level approach involves using ASP.NET internally to handle keeping users out. In ASP.NET 2.0 there’s the App_Offline.htm file that you can create in your application’s root directory. App_Offline.htm is a marker file for ASP.NET that it checks when requests are run: If the file is present the file is displayed. If not the application runs. It’s nice that there was some sort of thought give to keeping your app offline, but this mechanism is also a bit of a hassle as it involves changing a file on the server – you have to physically place or at least rename this file on the server in order for this to work. You could potentially automate the process of taking the app offline (click a button and create the App_Offline.htm file) but then there’s no way to take it back online.
One of the things I do in my internal Web framework is include a configuration setting for allowing an application to be taken offline through an Admin interface. There’s an a global static property App.ApplicationOffline which when set triggers the application not processing requests and only displaying a user defined message.
I use an ‘global’ App class to hold this information and other configuration and static information about the application which looks something like this:
public class App
{
public static string APP_NAME = "";
/// <summary>
/// Configuration object
/// </summary>
public static WebStoreConfig Configuration
{
get { return _Configuration; }
set { _Configuration = value; }
}
static WebStoreConfig _Configuration;
public static bool ApplicationOffline = false;
public static string ApplicationOfflineMessage =
"The application is offline temporarily for maintenance purposes.";
… more Configuration
}
The actual check is then hooked up in Application_BeginRequest (or you could create a module for this as well):
protected void Application_BeginRequest(Object sender, EventArgs e)
{
if (App.ApplicationOffline)
{
//if ( !Context.User.Identity.IsAuthenticated )
string Virtual = Request.Path.Substring(0,Request.Path.LastIndexOf("/")+1 );
if (Virtual.ToLower().IndexOf("/admin/") == -1)
MessageDisplay.DisplayMessage("Hold on",
App.ApplicationOfflineMessage +
@"<br /><br />
Usually these operations only take a few seconds, so please retry your request by using
the Refresh button.");
}
}
There’s another configuration property, which is the App.ApplicationOfflineMessage, which can be presented in the Configuration user interface for editing so a custom message can be set. On a configuration page an admi user can then set the message and toggle the App.ApplicationOffline flag on a Web page.
Notice that there’s some logic in the application that allows requests to the Admin interface to succeed. At the very least you’d need to allow access to the page that can be used to toggle the AppOffline switch, but I like the ability to actually access the admin interface of applications where additional application monitoring information usually is available.
There are advantages and disadvantages to this dynamic approach. It works great if you need to make changes to the data base or anything else that requires changes on the backend configuration. But this approach will not work for the situation where you are uploading ASP.NET assemblies and pages to the Web server. In that case the application will be in an unstable state and errors may occur if any assemblies that are required for operating the BeginRequest are involved. If you’re updating assemblies – especially the myriad of assemblies and .compiled files required for stock ASP.NET 2.0 applications – it possible in fact likely that the application will error out and in many cases fall back on the default ASP.NET Yellow Page of Death even if you have error handling built in to your app.
In that scenario the App_Offline.htm file is a better choice, as it will keep ANY ASP.NET code from running. The check for App_Offline.htm is one of the first things that happens in the pipeline so it will fire before any code or ASP.NET errors can even occur. But you’re stuck with the non-dynamic nature of this setup.
One possible workaround for this is to use another separate ASP.NET application to create and delete the App_Offline.Htm file. Another application (in a separate virtual) won’t be affected by the App_Offline.htm file and so it would be able to write and delete the file given the application has permissions to write in the directory.
I find myself needing both, but since I’ve recently switched to Web Application Projects with single assembly compiles I typically update just the WAP assembly and possibly one or two business object library assemblies. Copying these few files is usually pretty quick, so the potential for ASP.NET choking is much lower than with the many files of stock projects. I can live with that I suppose and so I tend to use the code mechanism above most of the time when doing things like database updates or heavy maintenance.