CustomErrors does not work when setting redirectMode=“ResponseRewrite”

前端 未结 10 1944
野趣味
野趣味 2020-11-27 11:51

In a old site, I was changing the way that CustomErrors works by adding redirectMode=\"ResponseRewrite\" (new in 3.5 SP1):



        
相关标签:
10条回答
  • 2020-11-27 12:28

    Due to the reliance on Server.Transfer it seems that the internal implementation of ResponseRewrite isn't compatible with MVC.

    This seems like a glaring functionality hole to me, so I decided to re-implement this feature using a HTTP module, so that it just works. The solution below allows you to handle errors by redirecting to any valid MVC route (including physical files) just as you would do normally.

    <customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
        <error statusCode="404" redirect="404.aspx" />
        <error statusCode="500" redirect="~/MVCErrorPage" />
    </customErrors>
    

    This has been tested on the following platforms;

    • MVC4 in Integrated Pipeline Mode (IIS Express 8)
    • MVC4 in Classic Mode (VS Development Server, Cassini)
    • MVC4 in Classic Mode (IIS6)

    namespace Foo.Bar.Modules {
    
        /// <summary>
        /// Enables support for CustomErrors ResponseRewrite mode in MVC.
        /// </summary>
        public class ErrorHandler : IHttpModule {
    
            private HttpContext HttpContext { get { return HttpContext.Current; } }
            private CustomErrorsSection CustomErrors { get; set; }
    
            public void Init(HttpApplication application) {
                System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
                CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");
    
                application.EndRequest += Application_EndRequest;
            }
    
            protected void Application_EndRequest(object sender, EventArgs e) {
    
                // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
                if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {
    
                    int statusCode = HttpContext.Response.StatusCode;
    
                    // if this request has thrown an exception then find the real status code
                    Exception exception = HttpContext.Error;
                    if (exception != null) {
                        // set default error status code for application exceptions
                        statusCode = (int)HttpStatusCode.InternalServerError;
                    }
    
                    HttpException httpException = exception as HttpException;
                    if (httpException != null) {
                        statusCode = httpException.GetHttpCode();
                    }
    
                    if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {
    
                        Dictionary<int, string> errorPaths = new Dictionary<int, string>();
    
                        foreach (CustomError error in CustomErrors.Errors) {
                            errorPaths.Add(error.StatusCode, error.Redirect);
                        }
    
                        // find a custom error path for this status code
                        if (errorPaths.Keys.Contains(statusCode)) {
                            string url = errorPaths[statusCode];
    
                            // avoid circular redirects
                            if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {
    
                                HttpContext.Response.Clear();
                                HttpContext.Response.TrySkipIisCustomErrors = true;
    
                                HttpContext.Server.ClearError();
    
                                // do the redirect here
                                if (HttpRuntime.UsingIntegratedPipeline) {
                                    HttpContext.Server.TransferRequest(url, true);
                                }
                                else {
                                    HttpContext.RewritePath(url, false);
    
                                    IHttpHandler httpHandler = new MvcHttpHandler();
                                    httpHandler.ProcessRequest(HttpContext);
                                }
    
                                // return the original status code to the client
                                // (this won't work in integrated pipleline mode)
                                HttpContext.Response.StatusCode = statusCode;
    
                            }
                        }
    
                    }
    
                }
    
            }
    
            public void Dispose() {
    
            }
    
    
        }
    
    }
    

    Usage

    Include this as the final HTTP module in your web.config

      <system.web>
        <httpModules>
          <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
        </httpModules>
      </system.web>
    
      <!-- IIS7+ -->
      <system.webServer>
        <modules>
          <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
        </modules>
      </system.webServer>
    
    0 讨论(0)
  • 2020-11-27 12:28

    I found that the problem was in Error.aspx. Still can't find what was the actual error in error.aspx that causes the problem.

    Changing the page to a static html file solved the problem.

    0 讨论(0)
  • 2020-11-27 12:28

    I built an error page in aspx that transfers the query to an ASP.NET MVC controller. You can rewrite the query to this aspx page and it will transfer the query to your custom controller.

    protected void Page_Load(object sender, EventArgs e)
    {
      //Get status code
      var queryStatusCode = Request.QueryString.Get("code");
      int statusCode;
      if (!int.TryParse(queryStatusCode, out statusCode))
      {
        var lastError = Server.GetLastError();
        HttpException ex = lastError as HttpException;
        statusCode = ex == null ? 500 : ex.GetHttpCode();
      }
      Response.StatusCode = statusCode;
    
      // Execute a route
      RouteData routeData = new RouteData();
      string controllerName = Request.QueryString.Get("controller") ?? "Errors";
      routeData.Values.Add("controller", controllerName);
      routeData.Values.Add("action", Request.QueryString.Get("action") ?? "Index");
    
      var requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
      IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext, controllerName);
      controller.Execute(requestContext);
    }
    

    Find more details here : https://stackoverflow.com/a/27354140/143503

    0 讨论(0)
  • 2020-11-27 12:31

    I know this question is a bit old, but I thought I should point out that it doesn't need to be a static file to get this working.

    I ran into a similar thing, and it's just a matter of finding that error in your Error.aspx, in our case it was because the masterpage in use relied on a piece of session data and when ResponseRewrite was set the session is not available to our Error.aspx page.

    I haven't worked out yet whether this unavailability of session is due to our specific app config or a "by design" part of ASP.net.

    0 讨论(0)
  • 2020-11-27 12:35

    The only way that worked perfectly for me is to turn off custom errors and replace iis's error pages via web.config. It sends the correct status code with the response and has the benefit of not going through the mvc.

    here's the code

    1. Turn off custom errors

      <customErrors mode="Off" />
      
    2. Replace error pages

      <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <remove statusCode="500" subStatusCode="-1" />
        <error statusCode="404" path="Error404.html" responseMode="File" />
        <error statusCode="500" path="Error.html" responseMode="File" />
      </httpErrors>
      

    Note. Use responsemode="file" if the url is a direct link to a file

    info : http://tipila.com/tips/use-custom-error-pages-aspnet-mvc

    0 讨论(0)
  • 2020-11-27 12:35

    What's happening is IIS is seing the error status code and presenting it's own error page instead of yours. To solve you need to set this in the code behind page of your error page to prevent IIS from doing this:

    Response.TrySkipIisCustomErrors = true;
    

    This will only work in IIS7 or above, for earlier versions of IIS you'll need to play with the error page settings.

    0 讨论(0)
提交回复
热议问题