CustomErrors não funciona ao configurar redirectMode = “ResponseRewrite”

Em um site antigo, eu estava mudando a forma como CustomErrors funciona adicionando redirectMode="ResponseRewrite" (novo no 3.5 SP1):

    

O problema é: ele mostra a página de erro genérica (aquela que você obtém quando não define customErrors . Se eu remover a parte redirectMode="ResponseRewrite" , ela funcionará bem.

Tenho certeza de que o 3.5 SP1 está instalado no servidor, porque eu uso a mesma configuração em outros sites hospedados no mesmo servidor.

Alguma ideia?

É importante observar para qualquer um que tente fazer isso em um aplicativo MVC que o ResponseRewrite usa o Server.Transfer nos bastidores. Portanto, o defaultRedirect deve corresponder a um arquivo legítimo no sistema de arquivos. Aparentemente, Server.Transfer não é compatível com rotas MVC, portanto, se sua página de erro é servida por uma ação de controlador, Server.Transfer vai procurar / Error / Whatever, não encontrá-lo no sistema de arquivos e retornar um genérico Página de erro 404!

A única maneira que funcionou perfeitamente para mim é desativar os erros personalizados e replace as páginas de erro da iis via web.config. Ele envia o código de status correto com a resposta e tem o benefício de não passar pelo mvc.

aqui está o código

  1. Desativar erros personalizados

      
  2. Substituir páginas de erro

           

Nota. Use responsemode="file" se o URL for um link direto para um arquivo

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

O que está acontecendo é que o IIS está vendo o código de status de erro e apresentando sua própria página de erro em vez da sua. Para resolver isso, é necessário definir isso na página de código da sua página de erro para evitar que o IIS faça isso:

 Response.TrySkipIisCustomErrors = true; 

Isso só funcionará no IIS7 ou acima, para versões anteriores do IIS você precisará jogar com as configurações da página de erro.

Devido à dependência do Server.Transfer , parece que a implementação interna do ResponseRewrite não é compatível com o MVC.

Isso parece um buraco de funcionalidade para mim, então decidi reimplementar esse recurso usando um módulo HTTP, para que ele funcione . A solução abaixo permite que você gerencie erros redirecionando para qualquer rota MVC válida (incluindo arquivos físicos) da mesma forma que faria normalmente.

     

Isso foi testado nas seguintes plataformas;

  • MVC4 no modo de pipeline integrado (IIS Express 8)
  • MVC4 no Modo Clássico (VS Development Server, Cassini)
  • MVC4 no modo clássico (IIS6)

 namespace Foo.Bar.Modules { ///  /// Enables support for CustomErrors ResponseRewrite mode in MVC. ///  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 errorPaths = new Dictionary(); 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() { } } } 

Uso

Inclua isso como o módulo HTTP final no seu web.config

             

Eu sei que esta questão é um pouco antiga, mas eu pensei que deveria apontar que não precisa ser um arquivo estático para que isso funcione.

Eu me deparei com uma coisa semelhante, e é apenas uma questão de encontrar esse erro em seu Error.aspx, no nosso caso, foi porque a masterpage em uso se baseou em uma parte dos dados da session e quando ResponseRewrite foi definida, a session não está disponível para nossa página Error.aspx.

Ainda não sei se essa indisponibilidade de session é devido à nossa configuração específica de aplicativo ou a uma parte “designada” do ASP.net.

Eu descobri que o problema estava no Error.aspx. Ainda não é possível encontrar qual foi o erro real em error.aspx que causa o problema.

Mudar a página para um arquivo html estático resolveu o problema.

Eu construí uma página de erro no aspx que transfere a consulta para um controlador ASP.NET MVC. Você pode rewrite a consulta para essa página aspx e ela transferirá a consulta para seu controlador personalizado.

 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); } 

Encontre mais detalhes aqui: https://stackoverflow.com/a/27354140/143503

No meu caso particular, minha página de erro tinha uma página mestra que tinha um controle de usuário que tentava usar a session. Se a Sessão não estiver disponível, você receberá uma HttpException: “O estado da session só pode ser usado quando enableSessionState estiver configurado como true, em um arquivo de configuração ou na diretiva Page.” A correção mais fácil é mudar para o HTML estático, a segunda correção mais fácil é usar uma página de erro mais simples, o mais difícil é garantir que sua página de erro não faça suposições em lugar algum (como essa session não lançará uma exceção) não pode errar.

Descobri que, se você usar redirectMode = “ResponseRewrite”, precisará adicionar algo na área de reconfiguração do arquivo web.config. Problema é quando seu site está quebrado! Você não pode rewrite URL, pois seu site não pode chamar o “virtual.aspx” que manipula sua reescrita!