Como fazer com que páginas de erro personalizadas funcionem no ASP.NET MVC 4


247

Quero uma página de erro personalizada mostrada para 500, 404 e 403. Aqui está o que fiz:

  1. Ativou erros personalizados no web.config da seguinte maneira:

    <customErrors mode="On" 
                  defaultRedirect="~/Views/Shared/Error.cshtml">
    
        <error statusCode="403" 
               redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
    
        <error statusCode="404" 
               redirect="~/Views/Shared/FileNotFound.cshtml" />
    
    </customErrors>
  2. Registrado HandleErrorAttributecomo um filtro de ação global na FilterConfigclasse da seguinte maneira:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
  3. Criou uma página de erro personalizada para cada uma das mensagens acima. O padrão para 500 já estava disponível imediatamente.

  4. Declarado em cada visualização de página de erro personalizada que o modelo para a página é System.Web.Mvc.HandleErrorInfo

Para 500, mostra a página de erro personalizada. Para outros, não.

Há algo que estou perdendo?

Parece que isso não é tudo o que há para exibir erros personalizados à medida que leio o código no OnExceptionmétodo da HandleErrorAttributeclasse e está lidando com apenas 500.

O que preciso fazer para lidar com outros erros?


21
O que é estranho nessa configuração é que você está redirecionando para visualizações, não para ações do controlador. Quem deveria renderizar essas visualizações e passar um modelo, por exemplo? Só pensando.
Oliver

2
A maioria das respostas aqui não trata de todos os casos ou faz com que o servidor da Web responda de maneira "incorreta", ou seja, redirecionando para uma página de erro em vez de retornar uma resposta de erro. Se você se importa com o servidor responder da maneira esperada dos servidores da Web, há um artigo bastante detalhado aqui: benfoster.io/blog/aspnet-mvc-custom-error-pages . Esteja avisado de que não é tão simples quanto as respostas aqui; portanto, se você quiser uma resposta fácil, basta usar uma das alternativas abaixo.
Rdans 23/05

1
Aqui está outro ótimo artigo sobre várias técnicas para o tratamento de erros do asp.net dusted.codes/…
Godsayah

Respostas:


352

Minha configuração atual (no MVC3, mas acho que ainda se aplica) depende de uma ErrorController, então eu uso:

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error">
      <error redirect="~/Error/NotFound" statusCode="404" />
    </customErrors>
</system.web>

E o controlador contém o seguinte:

public class ErrorController : Controller
{
    public ViewResult Index()
    {
        return View("Error");
    }
    public ViewResult NotFound()
    {
        Response.StatusCode = 404;  //you may want to set this to 200
        return View("NotFound");
    }
}

E as visualizações do jeito que você as implementa. Porém, costumo adicionar um pouco de lógica, para mostrar o rastreamento de pilha e as informações de erro, se o aplicativo estiver no modo de depuração. Então, Error.cshtml se parece com isso:

@model System.Web.Mvc.HandleErrorInfo
@{
    Layout = "_Layout.cshtml";
    ViewBag.Title = "Error";
}
<div class="list-header clearfix">
    <span>Error</span>
</div>
<div class="list-sfs-holder">
    <div class="alert alert-error">
        An unexpected error has occurred. Please contact the system administrator.
    </div>
    @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
    {
        <div>
            <p>
                <b>Exception:</b> @Model.Exception.Message<br />
                <b>Controller:</b> @Model.ControllerName<br />
                <b>Action:</b> @Model.ActionName
            </p>
            <div style="overflow:scroll">
                <pre>
                    @Model.Exception.StackTrace
                </pre>
            </div>
        </div>
    }
</div>

7
Você teve que colocar alguma coisa no seu Application_Error no seu Global.asax para esse Pablo?
Alicia

12
O código no controlador não parece ser executado a partir da minha experiência. MVC4 - lançar um System.Exception em um controlador diferente fará com que o arquivo Error.cshtml seja renderizado, mas não através do ErrorController. Alguém mais está experimentando isso?
Nilzor

53
Para quem mais achou isso útil, mas precisava de mais contexto; A tag <customErrors> entra em <system.web> no web.config.
gooberverse

7
Atualização para os outros - aparentemente o meu problema estava acontecendo porque eu tinha redirectMode="ResponseRewrite"sobre o CustomerErrorselemento
KyleMit

42
Por favor, pelo amor de Deus, ignore o comentário dizendo //you may want to set this to 200no código. NÃO FAÇA ISSO!
Dementic

40

Eu fiz a solução pablo e sempre tive o erro (MVC4)

A exibição 'Erro' ou seu mestre não foi encontrado ou nenhum mecanismo de exibição suporta o local pesquisado.

Para se livrar disso, remova a linha

 filters.Add(new HandleErrorAttribute());

em FilterConfig.cs


Eu olhei em todos os lugares para resolver isso. Isso finalmente teve a resposta. Eu sabia por que estava fazendo isso, mas para mim não podia, sem pensar drasticamente como o que as outras pessoas disseram. Imagino compartilhar a dor da 360Airwalk quando digo obrigado por apontar isso. Lenda!
Adam

Esta é uma opção e o controlador de erros funciona bem. Mas parece que quando você registra filtros no FilterConfig.cs, ele procura Error.cshtml nas pastas de exibição dos controladores compartilhados e originais. Quando você altera o Error.cshtml para algo diferente do nosso, o ErrorController personalizado funciona. Mas há um lugar em que você pode adicionar esse registro e é global.asax.cs. Se você adicionar a linha mencionada na função RegisterGlobalFilters (filtros GlobalFilterCollection) em global.asax.cs e remover do FilterConfig.cs, funcionará.
Isaolmez 2/10

Eu acho que está relacionado à ordem dos registros de filtro. Mantenha o controlador de erros e mova o registro do filtro para global.asax.cs. public static void RegisterGlobalFilters (filtros GlobalFilterCollection) {filtros.Add (novo HandleErrorAttribute ()); }
isaolmez

24

Eu faço algo que requer menos codificação do que as outras soluções postadas.

Primeiro, no meu web.config, tenho o seguinte:

<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
   <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
   <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>

E o controlador (/Controllers/ErrorPageController.cs) contém o seguinte:

public class ErrorPageController : Controller
{
    public ActionResult Oops(int id)
    {
        Response.StatusCode = id;

        return View();
    }
}

E, finalmente, a exibição contém o seguinte (simplificado, mas pode conter:

@{ ViewBag.Title = "Oops! Error Encountered"; }

<section id="Page">
  <div class="col-xs-12 well">
    <table cellspacing="5" cellpadding="3" style="background-color:#fff;width:100%;" class="table-responsive">
      <tbody>
        <tr>
          <td valign="top" align="left" id="tableProps">
            <img width="25" height="33" src="~/Images/PageError.gif" id="pagerrorImg">
          </td>
          <td width="360" valign="middle" align="left" id="tableProps2">
            <h1 style="COLOR: black; FONT: 13pt/15pt verdana" id="errortype"><span id="errorText">@Response.Status</span></h1>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth"><font style="COLOR: black; FONT: 8pt/11pt verdana">Possible causes:</font>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth2">
            <font style="COLOR: black; FONT: 8pt/11pt verdana" id="LID1">
                            <hr>
                            <ul>
                                <li id="list1">
                                    <span class="infotext">
                                        <strong>Baptist explanation: </strong>There
                                        must be sin in your life. Everyone else opened it fine.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Presbyterian explanation: </strong>It's
                                        not God's will for you to open this link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong> Word of Faith explanation:</strong>
                                        You lack the faith to open this link. Your negative words have prevented
                                        you from realizing this link's fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Charismatic explanation: </strong>Thou
                                        art loosed! Be commanded to OPEN!<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Unitarian explanation:</strong> All
                                        links are equal, so if this link doesn't work for you, feel free to
                                        experiment with other links that might bring you joy and fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Buddhist explanation:</strong> .........................<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Episcopalian explanation:</strong>
                                        Are you saying you have something against homosexuals?<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Christian Science explanation: </strong>There
                                        really is no link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Atheist explanation: </strong>The only
                                        reason you think this link exists is because you needed to invent it.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Church counselor's explanation:</strong>
                                        And what did you feel when the link would not open?
                                    </span>
                                </li>
                            </ul>
                            <p>
                                <br>
                            </p>
                            <h2 style="font:8pt/11pt verdana; color:black" id="ietext">
                                <img width="16" height="16" align="top" src="~/Images/Search.gif">
                                HTTP @Response.StatusCode - @Response.StatusDescription <br>
                            </h2>
                        </font>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</section>

É tão simples quanto isso. Ele pode ser facilmente estendido para oferecer informações de erro mais detalhadas, mas o ELMAH lida com isso para mim e o statusCode & statusDescription é tudo o que geralmente preciso.


Eu acho que o redirecionamento no arquivo .config de "~ / ErrorPage / Oops / 404" provavelmente deve ser "~ / ErrorPage / Oops? 404", certo? Pelo menos foi o que funcionou para mim. Talvez isso dependa apenas do roteamento.
Josh Sutterfield

Como simular um erro gerado pelo IIS. Seja 500 ou 504. O que fazer no código ASP.Net MVC - 5 para simular a exceção do IIS para que eu possa testar minha página de erro personalizada
Inquebrável

12

Parece haver uma série de etapas aqui misturadas. Vou apresentar o que fiz do zero.

  1. Crie o ErrorPagecontrolador

    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
  2. Adicione visualizações para essas duas ações (clique com o botão direito do mouse -> Adicionar exibição). Eles devem aparecer em uma pasta chamada ErrorPage.

  3. Dentro App_Start abra FilterConfig.cse comente o filtro de tratamento de erros.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Remove this filter because we want to handle errors ourselves via the ErrorPage controller
        //filters.Add(new HandleErrorAttribute());
    }
  4. Dentro do web.config, adicione o seguinte <customerErrors> entradas, emSystem.Web

    <customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
        <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
        <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
    </customErrors>
  5. Teste (claro). Lance uma exceção sem tratamento no seu código e veja-a ir para a página com o ID 500 e, em seguida, use um URL para uma página que não existe para ver 404.


Eu estava recebendo esse erro An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.Tudo o que peguei no seu código está no arquivo web.config, adicionei <error redirect = "~/ControllerName/ActionName" statusCode="404"/>e funcionou bem :) O restante do código foi da resposta de @ Pablo. Eu estou usando MVC 5 e estrutura de entidade 6. Eu não removeu filters.Add(new HandleErrorAttribute())a partirFilterConfig.cs
Sumedha

Como simular um erro gerado pelo IIS. Seja 500 ou 504. O que fazer no código ASP.Net MVC - 5 para simular a exceção do IIS para que eu possa testar minha página de erro personalizada
Inquebrável

Além disso, como lançar uma exceção não tratada (etapa 5). Eu sou novo na codificação, por favor, guie.
Inquebrável

Ainda não funciona para mim? E o roteamento? Também preciso adicionar a página Roteamento para erro? Se eu clicar na página: localhost: 84 / Enforcer / blah, sou redirecionado para: localhost: 84 / Enforcer / Enforcer / Error / NotFound? Aspxerrorpath = /… A página de erro se parece com uma página de erro padrão fornecida pelo Asp.NET. Alguma ideia?
Radek Strugalski

O elemento customerrors no webconfig deve estar acondicionando isso. Seu código de rota padrão (criado pelo projeto) deve funcionar bem.
VictorySaber

11

Eu recomendaria usar o arquivo Global.asax.cs.

 protected void Application_Error(Object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception is HttpUnhandledException)
    {
        Server.Transfer("~/Error.aspx");
    }
    if (exception != null)
    {
        Server.Transfer("~/Error.aspx");
    }
    try
    {
        // This is to stop a problem where we were seeing "gibberish" in the
        // chrome and firefox browsers
        HttpApplication app = sender as HttpApplication;
        app.Response.Filter = null;
    }
    catch
    {
    }
}

1
Eu não acho que você poderia fazer um Server.Transfer () no MVC. Você acha que o OP tem um site misto?
Rap

1
por que devemos usar Application_Error no mvc? Temos opções como [handleerror] attribute com opções de URL de redirecionamento. Existe alguma vantagem específica com application_error nisso?
Kurkula 3/02

Devemos usar HandleErrorAttribute no MVC e, substituindo o método OnException, podemos manipulá-los de uma maneira muito melhor
Kumar Lachhani

7

Com base na resposta postada por maxspan, montei um projeto de amostra mínimo no GitHub mostrando todas as partes de trabalho.

Basicamente, apenas adicionamos um Application_Errormétodo ao global.asax.cs para interceptar a exceção e nos dá a oportunidade de redirecionar (ou mais corretamente, solicitar a transferência ) para uma página de erro personalizada.

    protected void Application_Error(Object sender, EventArgs e)
    {
        // See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4
        // for additional context on use of this technique

        var exception = Server.GetLastError();
        if (exception != null)
        {
            // This would be a good place to log any relevant details about the exception.
            // Since we are going to pass exception information to our error page via querystring,
            // it will only be practical to issue a short message. Further detail would have to be logged somewhere.

            // This will invoke our error page, passing the exception message via querystring parameter
            // Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above.
            // As an alternative, Response.Redirect could be used instead.
            // Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 )
            Server.TransferRequest("~/Error?Message=" + exception.Message);
        }

    }

Controlador de erro:

/// <summary>
/// This controller exists to provide the error page
/// </summary>
public class ErrorController : Controller
{
    /// <summary>
    /// This action represents the error page
    /// </summary>
    /// <param name="Message">Error message to be displayed (provided via querystring parameter - a design choice)</param>
    /// <returns></returns>
    public ActionResult Index(string Message)
    {
        // We choose to use the ViewBag to communicate the error message to the view
        ViewBag.Message = Message;
        return View();
    }

}

Visualização da página de erro:

<!DOCTYPE html>

<html>
<head>
    <title>Error</title>
</head>
<body>

    <h2>My Error</h2>
    <p>@ViewBag.Message</p>
</body>
</html>

Nada mais está envolvido, além de desabilitar / remover filters.Add(new HandleErrorAttribute())no FilterConfig.cs

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute()); // <== disable/remove
    }
}

Embora seja muito simples de implementar, a única desvantagem que vejo nessa abordagem é usar a string de consulta para fornecer informações de exceção à página de erro de destino.


3

Eu tinha tudo configurado, mas ainda não conseguia ver as páginas de erro apropriadas para o código de status 500 em nosso servidor intermediário, apesar de tudo ter funcionado bem nos servidores de desenvolvimento local.

Encontrei esta postagem no blog de Rick Strahl que me ajudou.

Eu precisava adicionar Response.TrySkipIisCustomErrors = true;ao meu código de tratamento de erros personalizado.


@ Shaun314 Você quer dizer onde você coloca esse código? Na ação que lida com a solicitação. Você pode ver exemplos nessa postagem do blog.
DCShannon

2

Aqui está a minha solução. Use [ExportModelStateToTempData] / [ImportModelStateFromTempData] é desconfortável na minha opinião.

~ / Views / Home / Error.cshtml:

@{
    ViewBag.Title = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Error</h2>
<hr/>

<div style="min-height: 400px;">

    @Html.ValidationMessage("Error")

    <br />
    <br />

    <button onclick="Error_goBack()" class="k-button">Go Back</button>
    <script>
        function Error_goBack() {
            window.history.back()
        }
    </script>

</div>

~ / Controladores / HomeController.sc:

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Error()
    {
        return this.View();
    }

    ...
}

~ / Controladores / BaseController.sc:

public class BaseController : Controller
{
    public BaseController() { }

    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            if (filterContext.Controller.TempData.ContainsKey("Error"))
            {
                var modelState = filterContext.Controller.TempData["Error"] as ModelState;
                filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair<string, ModelState>("Error", modelState) });
                filterContext.Controller.TempData.Remove("Error");
            }
        }
        if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
        {
            if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error"))
            {
                filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"];
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

~ / Controladores / MyController.sc:

public class MyController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Details(int id)
    {
        if (id != 5)
        {
            ModelState.AddModelError("Error", "Specified row does not exist.");
            return RedirectToAction("Error", "Home");
        }
        else
        {
            return View("Specified row exists.");
        }
    }
}

Desejo-lhe projetos de sucesso ;-)


2

Você pode obter erros funcionando corretamente sem hackear o global.cs, mexendo com HandleErrorAttribute, executando Response.TrySkipIisCustomErrors, conectando Application_Error ou o que quer que seja:

No system.web (apenas o habitual, ativado / desativado)

<customErrors mode="On">
  <error redirect="/error/401" statusCode="401" />
  <error redirect="/error/500" statusCode="500" />
</customErrors>

e em system.webServer

<httpErrors existingResponse="PassThrough" />

Agora, as coisas devem se comportar conforme o esperado, e você pode usar o ErrorController para exibir o que precisar.


Como simular um erro gerado pelo IIS. Seja 500 ou 504. O que fazer no código ASP.Net MVC - 5 para simular a exceção do IIS para que eu possa testar minha página de erro personalizada
Inquebrável

@ Inquebrável altere temporariamente seu código para gerar uma exceção.
theycallmemorty

Não fez diferença para mim. Não fui levado para a minha página de erro personalizada por uma exceção ou pelo erro 404 não encontrado.
pnizzle 28/05/19

0

Parece que cheguei atrasado à festa, mas é melhor você também conferir isso.

Portanto, system.webpara armazenar em cache exceções no aplicativo, como return HttpNotFound ()

  <system.web>
    <customErrors mode="RemoteOnly">
      <error statusCode="404" redirect="/page-not-found" />
      <error statusCode="500" redirect="/internal-server-error" />
    </customErrors>
  </system.web>

e system.webServerpara recuperar erros que foram capturados pelo IIS e não chegaram à estrutura asp.net

 <system.webServer>
    <httpErrors errorMode="DetailedLocalOnly">
      <remove statusCode="404"/>
      <error statusCode="404" path="/page-not-found" responseMode="Redirect"/>
      <remove statusCode="500"/>
      <error statusCode="500" path="/internal-server-error" responseMode="Redirect"/>
  </system.webServer>

No último, se você se preocupar com a resposta do cliente, altere responseMode="Redirect"para responseMode="File"e sirva um arquivo html estático, pois este exibirá uma página amigável com um código de resposta 200.


0

No web.config, adicione isso na tag system.webserver como abaixo,

<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404"/>
  <remove statusCode="500"/>
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound"/>
  <error statusCode="500" responseMode="ExecuteURL"path="/Error/ErrorPage"/>
</httpErrors>

e adicione um controlador como,

public class ErrorController : Controller
{
    //
    // GET: /Error/
    [GET("/Error/NotFound")]
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;

        return View();
    }

    [GET("/Error/ErrorPage")]
    public ActionResult ErrorPage()
    {
        Response.StatusCode = 500;

        return View();
    }
}

e adicionar suas visões respeitadas, isso funcionará definitivamente, eu acho que para todos.

Esta solução foi encontrada em: Neptune Century

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.