skip to content
Stylized letter A in teal with pink sparkle accents Khalid Abuhakmeh

Take Your ASP.NET MVC Application Offline via a Global Attribute

/ 4 min read

Found this post helpful?

Buy me a coffee
Table of Contents

Failure is inevitable when building applications. ASP.NET has the ability to take an application offline when things have really gone past the point of recovery. I’ll let Scott Guthrie explain how it works.

Basically, if you place a file with this name [app_offline.htm] in the root of a web application directory, ASP.NET 2.0 will shut-down the application, unload the application domain from the server, and stop processing any new incoming requests for that application. ASP.NET will also then respond to all requests for dynamic pages in the application by sending back the content of the app_offline.htm file. – Scott Guthrie

While this can work, there are disadvantages to this approach:

  1. All requests to your site will be circumvented, which includes requests for CSS, JavaScript, and images. As Scott Gu mentioned, all requests are circumvented.
  2. No access to anything that ASP.NET offers; it is a static html file.

What I want to do, when I take my app offline:

  1. Return a 503 Service Unavailable HTTP status code.
  2. Circumvent all requests to my MVC Controllers, but not calls static files.
  3. Use the same layout of my existing application to keep a consistent look.
  4. Use C# / Razor to generate a more robust message page.

Normally the point of making the application unavailable is an issue with an external dependency, like a database.

Code

Let’s start with a class that will help us trigger whether the application is online or offline. When in offline mode, we want to avoid doing the same tasks that need performed when the app is online. Here is the code in action.

Available.Execute(
online: () =>
{
DatabaseConfiguration.Start();
},
offline: () =>
{
GlobalFilters.Filters.Add(new OfflineMessageAttribute());
});

Each action will be executed depending on the state of the application. The online action performs our database configuration, while the offline registers the OfflineMessageAttribute globally, we’ll see that implementation later. Here is the implementation of the Available class. The availability of the application is triggered via application settings in the web.config.

public static class Available
{
private static readonly Lazy<bool> _isOffline = new Lazy<bool>(() =>
{
bool result;
return bool.TryParse(
ConfigurationManager.AppSettings["available.offline"], out result) && result;
});
private static readonly Lazy<string> _message = new Lazy<string>(() => ConfigurationManager.AppSettings["available.message"]);
public static bool IsOffline
{
get { return _isOffline.Value; }
}
public static bool IsOnline
{
get { return !_isOffline.Value; }
}
public static string Message
{
get { return _message.Value; }
}
public static bool HasMessage
{
get { return !string.IsNullOrWhiteSpace(Message); }
}
public static void Execute(Action online, Action offline)
{
if (IsOffline)
offline();
else
online();
}
}

Nothing groundbreaking here, we just execute the offline action when the app is offline and the online action when the app is online. Now let’s look at the OfflineMessageAttribute implementation.

public class OfflineMessageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
filterContext.Result = new ContentResult {Content = string.Empty};
}
else
{
filterContext.Result = new ViewResult
{
ViewName = "Offline"
};
var response = filterContext.HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
response.TrySkipIisCustomErrors = true;
}
}
}

The attribute above intercepts all external requests and returns a ViewResult immediately, with a view of Offline. For child requests, it just return an empty ContentResult; this behavior can be modified for your specific needs. Note, the HTTP status code is now set to 503 Service Unavailable. Now all we need is a view.

@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<div class="row">
<div class="col-md-12 text-center">
<h2>Our Service Is Currently Unavailable</h2>
<h3>We assure you we are working with urgency to quickly resolve the issue.</h3>
@if (Available.HasMessage)
{
<p>@Available.Message</p>
}
</div>
</div>
</div>

Conclusion

While the original app_offline.htm functionality is a good approach to putting any ASP.NET application into maintenance mode, it takes away a lot of what makes ASP.NET great for building pages. By using the approach outlined in this post, we can still inform our users that their are issues with our application with our best assets: logging, views, css, images, etc. Obviously, each application is different and there is no one way to degrade gracefully but isolating external dependencies is a good start.

Related Posts