ふるすたっくえんじにあっぽい人の日記

ASP .NET MVC (C#)、.NET Framework、iOS (Objective-c) アプリ、Androidアプリ (Java)、AWS、たまにLAMPとかプロジェクトマネジメントあたりのお話

【ASP.NET】メンテナンスページを表示する方法5選

ひさびさASP.NET
グ○シーに出てきそうなタイトル。

1. URL Rewrite

Web.configに以下追記。
ファイルの有無で判定する感じですね。
maintenance.txtが存在してればmaintenance.htmlの内容が表示されます。
まぁ実務じゃあんま使わないかと。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Maintenance" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="true"/>
          <conditions>
            <add input="{APPL_PHYSICAL_PATH}maintenance.txt" matchType="IsFile" />
          </conditions>
          <action type="Rewrite" url="maintenance.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

2.IHttpModule + Web.config

おもむろにこんなクラスを用意します。

using System;
using System.Configuration;
using System.IO;
using System.Web;

namespace Hoge.Modules
{
    public class MaintenanceModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += Context_BeginRequest;
        }

        private void Context_BeginRequest(object sender, EventArgs e)
        {
            if (!bool.Parse(ConfigurationManager.AppSettings[@"InMaintenance"]))
                return;
            
            var app = (HttpApplication)sender;
            app.Context.Response.ContentType = @"text/html";
            app.Context.Response.WriteFile(
                Path.Combine(app.Request.ApplicationPath, @"maintenance.html"));
            app.Context.Response.End();
        }
    }
}

で。Web.config
nameはてきとーでよさげ?typeにフルパス指定で

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="InMaintenance" value="true"/>
  </appSettings>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="MaintenanceModule" type="Hoge.Modules.MaintenanceModule"/>
    </modules>
  </system.webServer>
</configuration>

InMaintenanceがtrueに設定されてればmaintenance.htmlの内容が表示されます。
別にWeb.configじゃなくDBとか他のとこから値取ってきてもいいですね。

3.Global.asax Server.Transferバージョン

判定は2と同じですぜ。

using System;
using System.Configuration;
using System.IO;
using System.Web;

namespace Hoge.App
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            if (!bool.Parse(ConfigurationManager.AppSettings[@"InMaintenance"]))
                return;

            Server.Transfer(Path.Combine(Context.Request.ApplicationPath, @"maintenance.html"));
        }
    }
}

4.Global.asax Response.Redirectバージョン

3の

Server.Transfer(Path.Combine(Context.Request.ApplicationPath, @"maintenance.html"));

Response.Redirect("http://www.hoge.com/maintenance.html");

に書き換え。
1~3と違って書き換えではなくリダイレクトですねー。当たり前ですねー。

5.DelegatingHandler

おもむろにこんなクラスを用意しましょう。

using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Dispatcher;

namespace Hoge.Handler
{
    public class MaintenanceHandler : DelegatingHandler
    {
        public MaintenanceHandler(HttpConfiguration configuration)
        {
            InnerHandler = new HttpControllerDispatcher(configuration);
        }
        
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken)
        {
            if (bool.Parse(ConfigurationManager.AppSettings[@"InMaintenance"]))
            {
                var maintenanceFilePath = Path.Combine(HttpRuntime.AppDomainAppPath, @"maintenance.html");
                var response = requestMessage.CreateResponse(HttpStatusCode.ServiceUnavailable);
                response.Content = new StringContent(File.ReadAllText(maintenanceFilePath));
                response.Content.Headers.ContentType = new MediaTypeHeaderValue(@"text/html");

                return Task<HttpResponseMessage>.Factory.StartNew(() => response);
            }

            return base.SendAsync(requestMessage, cancellationToken);
        }
    }
}

Global.asax.cs

using Hoge.Handler;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Hoge.App
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {           
            GlobalConfiguration.Configuration.Routes.MapHttpRoute(
                name: "Hoge",
                routeTemplate: "{group}/{controller}/{action}",
                defaults: new { action = UrlParameter.Optional },
                constraints: null,
                handler: new MaintenanceHandler(GlobalConfiguration.Configuration)
            );

            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);       
        }
    }
}

いじょー
2か5が使いやすいかなー
つかれたー。