Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
142 views
in Technique[技术] by (71.8m points)

c# - ASP.NET MVC Custom Errors

My goal is to create an error handling within the application that handles all managed errors, not just MVC related. So I'm not using the HandleErrorAttribute pattern because it is only for MVC errors, not other managed errors.

In my Global.asax I have:

protected void Application_Error()
{
    string displayError = ConfigurationManager.AppSettings["DisplayError"];
    NameValueCollection serverVariables;
    int loop1, loop2;
    StringBuilder serverVariableKeys = new StringBuilder();
    Exception exception = Server.GetLastError();
    HttpException httpException = exception as HttpException;

    if (HttpContext.Current != null)
    {
            // loop through and get server vars
    }

    if (exception != null)
        // Log Error

    // Redirect to Error page if set to basic mode
    if (!string.IsNullOrWhiteSpace(displayError) && displayError.ToLower() == "basic")
    {
        Response.Clear();
    Server.ClearError(); // needed for redirect to work

    var routeData = new RouteData();
    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;

    if (httpException != null)
    {
       Response.StatusCode = httpException.GetHttpCode();
       switch (Response.StatusCode)
        {
        case 403:
          routeData.Values["action"] = "Http403";
          break;
            case 404:
           routeData.Values["action"] = "Http404";
           break;
        }
    }

    IController errorsController = new Controllers.ErrorController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
    }
}

I have an ErrorController that has General, Http403 and Http404 Actions. I have views that correspond to each action.

In my web.config I have:

  <system.web>  
    <!-- removed bits to reduce space -->
    <customErrors mode="On" />
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules >
      <remove name="UrlRoutingModule-4.0" />
      <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
      <remove name="Session"/>
      <add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition=""/>
    </modules>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <httpErrors errorMode="Detailed" />
  </system.webServer>

This works for many scenarios related to managed code, but when IIS takes over, such as bad static file call (example.com/BadFileName.gif) it will return an error from IIS that discloses server details such as local file path. This is because of the following line in the web.config:

<httpErrors errorMode="Detailed" />

But when I set errorMode to other values such as Custom, or just remove the line, my custom error handling stops functioning and the default IIS errors messages are returned for everything including managed code errors.

I've tried a number of things to get to the Custom Errors to work without this setting without any luck, including:

  • Setting CustomError to mode="Off"
  • Defining Error nodes under customError to redirect to my controllers
  • Defining Error nodes under httpErrors to redirect to my controllers

When I step through in debugging I can see the call to the controller, but in the end IIS sends the response instead. The error logging does get called correctly Application_Error, so the code does let application handle it to certain point.

Application Details:

  • ASP.NET MVC 4.0
  • Hosted on IIS 7.5 (Azure Web Site Preview)

Is there way not use <httpErrors errorMode="Detailed" /> and make this work?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The reason you're seeing the behaviour is probably that your Application_Error handler is correctly being invoked (and invoking your controller), but then IIS is intercepting the 500 error and displaying its own error page. (though it's also possible that your Application_Error handler is itself throwing an Exception, that's something you'll want to rule out)

Because you want to handle all errors (e.g. the static file error you mentioned), you will likely be unable to rely on either ASP.NET or ASP.NET MVC to do the initial error detection for you. Not all errors come from your application!

The best thing to do is to lean on IIS 7.5's error handling as much as you can, rather than try to get involved yourself too early. Let IIS do the initial error detection for you and it's much easier.

Try putting the following in your web.config, replacing your existing httpErrors node:-

<httpErrors errorMode="Custom" existingResponse="Replace">
  <clear />
  <error statusCode="400" responseMode="ExecuteURL" path="/Error" />
  <error statusCode="403" responseMode="ExecuteURL" path="/Error/Forbidden" />
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound" />
  <error statusCode="500" responseMode="ExecuteURL" path="/Error" />
</httpErrors>

Remove the customErrors node entirely. It is unnecessary.

Remember and make sure your error controller is setting the correct status code:-

public ActionResult Forbidden()
{
    Response.StatusCode = 403;
    return this.View();
}

You should then be able to ditch everything apart from the logging out of your Application_Error handler - though I think you'd be best creating your own HttpModule; setting runAllManagedModulesForAllRequests on the modules node in your web.config helps you catch errors that would otherwise slip through.

I've got this setup (though I'm personally using elmah to do the logging) running on the Azure Web Sites preview, and it's catching everything except from obviously any errors that come from failing to parse web.config.

I've put a full working example of using httpErrors for custom error pages up on github. You can also see it live on azure web sites.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...