Enable custom errors in Azure

2019-01-25 20:13发布

问题:

I am unable to set IIS to serve my custom error pages for errors outside of the MVC pipeline. If I throw an exception inside a controller it's all file, the Application_Error event handles that:

    protected void Application_Error(object sender, EventArgs e)
    {
        var error = Server.GetLastError();

        var routeData = new RouteData();
        routeData.Values["Controller"] = "Error";
        routeData.Values["Area"] = "";
        routeData.Values["Exception"] = error;

        if (error is HttpException)
        {
            switch (((HttpException)error).GetHttpCode())
            {
                case 401:
                    routeData.Values["Action"] = "NotAllowed";
                    break;

                case 403:
                    routeData.Values["Action"] = "NotAllowed";
                    break;

                case 404:
                    routeData.Values["Action"] = "NotFound";
                    break;
                default:
                    routeData.Values["Action"] = "ServerError";
                    break;
            }
        }
        else
        {
            routeData.Values["Action"] = "ServerError";
        }

        Response.Clear();
        Server.ClearError();

        IController controller = new ErrorController();
        controller.Execute(new RequestContext(new HttpContextWrapper(((MvcApplication)sender).Context), routeData));
    }

However, if I browse to a non-existent URL, IIS will handle the 404 error (or any other error), giving me the standard IIS error message and completely ignoring my web.config settings:

 <system.web>
  <customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/error">
    <error statusCode="401" redirect="/error/notallowed" />
    <error statusCode="403" redirect="/error/notallowed" />
    <error statusCode="404" redirect="/error/notfound" />
    <error statusCode="500" redirect="/error/servererror" />
  </customErrors>
</system.web>

<system.webServer>
  <httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <clear />
    <error statusCode="401" path="/error/notallowed" />
    <error statusCode="403" path="/error/notallowed" />
    <error statusCode="404" path="/error/notfound" />
    <error statusCode="500" path="/error/servererror" />
  </httpErrors>
</system.webServer>

/error/* is handled by a controller inside my application. What can I do to make IIS execute the custom error path, instead of feeding me the standard error page?

This is ASP.NET MVC 3, running on Azure. It doesn't work under straight IIS either, but the development server does execute the controller.

回答1:

IIS does not respect defaultResponseMode. You must add responseMode to each and every error, like so:

<httpErrors errorMode="Custom">
  <clear />
  <error statusCode="401" path="/error/notallowed" responseMode="ExecuteURL" />
  <error statusCode="403" path="/error/notallowed" responseMode="ExecuteURL" />
  <error statusCode="404" path="/error/notfound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/error/servererror" responseMode="ExecuteURL" />
</httpErrors>

Why default doesn't apply is beyond me.



回答2:

I had exactly this problem and I was on Azure, the following solved it for me:

    Response.TrySkipIisCustomErrors = true;


回答3:

I found many solution accross web, but neither was working also for static files (for example for non-existing 'picture.jpg').

This solution works for me on both Azure WebSites and also on Azure WebRole instance. I use web.config transformations so update it to you own needs accordingly.

I use ASPX pages for errors so I can set correct HTTP response code (IIS always sets HTTP 200, when I used .htm pages)

File ~/404.aspx is:

<%@ Page Language="C#" AutoEventWireup="false" ContentType="text/html" Debug="false" EnableEventValidation="false" EnableSessionState="False" EnableViewState="false" ResponseEncoding="utf-8" Transaction="Disabled" ValidateRequest="false" ViewStateMode="Disabled" %>
<% Context.Response.StatusCode = 404; %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>404 Error - Page not found</title>
</head>
<body>
  <h1>Custom 404 page</h1>
</body>
</html>

File ~/500.aspx is:

<%@ Page Language="C#" AutoEventWireup="false" ContentType="text/html" Debug="false" EnableEventValidation="false" EnableSessionState="False" EnableViewState="false" ResponseEncoding="utf-8" Transaction="Disabled" ValidateRequest="false" ViewStateMode="Disabled" %>
<% Context.Response.StatusCode = 500; %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>500 Server Error</title>
</head>
<body>
  <h1>Custom 500 page</h1>
</body>
</html>

Neccessary parts from ~/Web.config contains:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <customErrors mode="Off" />
  </system.web>

  <system.webServer>
    <httpErrors errorMode="Detailed" />
  </system.webServer>
</configuration>

Neccessary parts from ~/Web.Release.config contains:

<?xml version="1.0"?>
<configuration>
  <customErrors mode="On" defaultRedirect="/500.aspx" redirectMode="ResponseRewrite" xdt:Transform="Replace">
    <!-- AWARE do NOT use address in form "~/404.htm" but in "/404.htm" -->
    <error statusCode="404" redirect="/404.aspx" />
    <error statusCode="500" redirect="/500.aspx"/>
  </customErrors>

  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace" xdt:Transform="Replace">
      <!-- AWARE do NOT use address in form "~/404.htm" but in "/404.htm" -->
      <remove statusCode="404" />
      <error statusCode="404" path="/404.aspx" responseMode="ExecuteURL" />

      <remove statusCode="500" />
      <error statusCode="500" path="/500.aspx" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

The end.



回答4:

Make sure you have set this setting.

<system.webServer>

    <modules runAllManagedModulesForAllRequests="true"/>

</system.webServer>

Otherwise your IIS application could also be running in classic mode instead of integrated mode, which is an IIS setting, however I thought integrated mode was the default in Azure (but to be honest I haven't checked)