Asp.net MVC网络应用程序的“Down for Maintenance”页面

时间:2014-03-11 22:08:40

标签: asp.net-mvc-3 iis-7.5 httpmodule action-filter

我有以下要求

  • 在web.config中设置一个值并启用维护模式
  • 所有非ajax请求都应显示自定义错误页面,http状态代码设置为503.应保留页面的url。
  • 所有ajax请求都应使用http状态代码503
  • 进行响应
  • 我应该有机会对文件进行一些基本的日志记录。如果他正好登录应用程序,请记录URL和用户标识
  • 我正在使用ELMAH跟踪/记录所有未处理的异常。实现维护模式的机制不应该让我不要使用ELMAH
  • 我将“runAllManagedModulesForAllRequests”设置为true。这最初是为了与RequestReduce一起使用。我们不再使用它,但我犹豫是否将其值重置为false。我不确定是否有其他图书馆需要它。

一旦我意识到没有任何内置支持上述要求,我觉得我面前有两个选项(App_offile.html对我不起作用)。

  • HttpModule
  • MVC ActionFilter

我放弃了MVC ActionFilter因为我无法弄清楚如何保证它在任何身份验证/授权过滤器之前运行。我有一个自定义身份验证筛选器,它可以访问db。维护模式背后的想法是数据库可能处于脱机状态,但是网络应用程序不应显示500个自定义错误页面,而应显示503自定义错误页面。

我写了以下httpmodule并添加到我的web.config中。它适用于ajax请求。它有点适用于非ajax请求。所有请求都会重定向到503错误页面。副作用是对静态内容的所有请求也导致503.我的错误页面因此显示没有样式:(

// the http module

public class MaintenanceModeModule : IHttpModule
{
    private static bool _isUnderMaintenance;
    static MaintenanceModeModule()
    {
        var valueStr = (ConfigurationManager.AppSettings["UnderMaintenance"] ?? (false).ToString());
        bool underMaintenance;
        bool.TryParse(valueStr, out underMaintenance);
        _isUnderMaintenance = underMaintenance;
    }

    public void Init(HttpApplication application)
    {
        application.BeginRequest += OnBeginRequest;
    }

    private void OnBeginRequest(object sender, EventArgs e)
    {
        var application = (HttpApplication) sender;
        var request = application.Request;
        var response = application.Response;

        if (_isUnderMaintenance == false)
        {
            return;
        }
        application.Context.Items["under_maintenance"] = true; // used later
        if (request.Url.PathAndQuery == "/503") // the url of the action that renders the custom error page
        {
            return;
        }

        const int statusCode = (int) HttpStatusCode.ServiceUnavailable;
        const string statusMessage = "Temporarily down for maintenance";

        var requestWrapper = new HttpRequestWrapper(request);
        if (requestWrapper.IsAjaxRequest())
        {
            response.Clear();
            response.ClearContent();
            response.ClearHeaders();
            response.StatusCode = statusCode;
            response.TrySkipIisCustomErrors = true;
            response.StatusDescription = statusMessage;
            response.End();
            return;
        }

        // doesn't work, shows the Yellow Screen of Death (YSoD)
        // application.Context.Server.Transfer("~/503", preserveForm: true);

        // doesn't work, shows the Yellow Screen of Death (YSoD)
        // throw new HttpException(statusCode, statusMessage); 

        response.Redirect("~/503");
    }

    public void Dispose()
    {

    }
}

...

// web.config
// only the relevant portions of each section is shown

<appSettings>
    <add key="UnderMaintenance" value="true" />
</appSettings>
<customErrors mode="On">  <!-- Custom errors are on, even then I was seeing YSoDs during my attempts -->
    <error statusCode="404" redirect="404" />
    <error statusCode="503" redirect="503" />
</customErrors>
<system.webServer>
    <httpErrors existingResponse="PassThrough">
    </httpErrors>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="MaintenanceMode" type="WebApp.Code.MvcInfrastructure.MaintenanceModeModule" />
    </modules>
</system.webServer>

...

// route config

routes.MapRoute("503Error", "503", new { controller = "Error", action = "UnderMaintenance" });

...

// error controller

// the authentication filter skips authentication if the allowanonymous attribute is present
[AllowAnonymous]
public class ErrorController : CustomBaseController
{
    public ErrorController(AppConfig appConfig)
        : base(appConfig)
    {
    }

    public ActionResult UnderMaintenance()
    {
        // behind the scenes reads the value from HttpContext.Items. 
        // This was set during the execution of the httpmodule
        if (AppConfig.UnderMaintenance == false) 
        {
            return new RedirectResult("~/");
        }

        Response.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
        Response.TrySkipIisCustomErrors = true;

        // the actual content of the view is not relevant now
        return View("Error503");
    }
}

这种方法的问题,

  1. 每个非ajax请求都以302然后是503
  2. 响应
  3. 不保留浏览器请求的网址
  4. 它还为所有静态资产返回503
  5. 我编写的代码和我启用的web.config设置都是从​​各种来源拼凑而成的。我不完全确定这些设置的作用或建议的方式。只要符合规定的要求,请随意用完全不同的方法回答。

0 个答案:

没有答案