Roteamento para página de erro personalizada ASP.NET MVC 404

116

Estou tentando fazer uma página de erro HTTP 404 personalizada quando alguém digita uma URL que não invoca uma ação ou controlador válido no ASP.NET MVC, em vez de exibir o erro genérico de ASP.NET "Recurso não encontrado".

Não quero usar o web.config para lidar com isso.

Existe algum tipo de mágica de roteamento que eu possa fazer para capturar URLs inválidos?

Atualização: tentei a resposta dada, mas ainda recebo a mensagem feia "Recurso não encontrado".

Outra atualização: Ok, aparentemente algo mudou no RC1. Eu até tentei especificamente capturar 404 em um HttpExceptione ainda me mostra a página "Recurso não encontrado".

Eu até usei o recurso de recurso do MvcContrib e nada - mesmo problema. Alguma ideia?

dswatik
fonte
@Peter Essa é a solução que adicionei junto com a substituição da HandleUnknownAction para mostrar uma visualização de página não encontrada quando uma ação não existe e, em seguida, o manipulador de erros personalizado do ASP.net integrado para lidar com qualquer outra coisa que os usuários possam digitar .
dswatik
@pete Isso também funciona stackoverflow.com/questions/619895/…
dswatik
8
Eu odeio quando outros usuários são presunçosos e dizem coisas como "por que você quer fazer isso? Apenas faça isso ..." Mas eu sugiro que, se nada o impedir de usar a abordagem web.config e satisfaz suas necessidades, é uma abordagem padrão e elegante. Agradecemos qualquer feedback sobre essa abordagem, pois pode haver algum problema que eu não conheço.
Sean

Respostas:

10

Basta adicionar "pegar todas as rotas" no final da tabela de rotas e exibir a página desejada com ela.

Veja: Como posso fazer uma rota "pega-tudo" para lidar com consultas '404 página não encontrada' para ASP.NET MVC?

Alex Reitbort
fonte
No entanto, tentei fazer isso, mas ainda recebo a horrível Mensagem de recurso não encontrado padrão do ASP.NET ..
dswatik
3
Não encontro uma solução definitiva (e flexível !!) para este problema nem nesta resposta nem no link fornecido.
Peter,
4
Nem eu ... isso parece uma resposta tipo hacky. Estou procurando mais uma maneira de fazer customErrors se comportar como em WebForms. Alguma ideia?
JC Grubbs
1
Isso não funciona e é ruim por princípio. Não funciona porque não detecta URLs ruins que correspondem a um dos padrões de roteamento anteriores. Ruim por princípio porque converte o que deveria ser um erro em não um erro. Redirecionar para uma página chamada "Erro" é diferente de redirecionar para uma página de erro. Você deseja mantê-lo como um erro, registrar o erro e tratá-lo como um erro. Erros são informações valiosas.
Matthew
105

Eu tentei habilitar erros personalizados no servidor de produção por 3 horas, parece que encontrei a solução final de como fazer isso na ASP.NET MVC sem nenhuma rota.

Para habilitar erros personalizados no aplicativo ASP.NET MVC, precisamos (IIS 7+):

  1. Configure páginas personalizadas na configuração da web na system.webseção:

    <customErrors mode="RemoteOnly"  defaultRedirect="~/error">
        <error statusCode="404" redirect="~/error/Error404" />
        <error statusCode="500" redirect="~/error" />
    </customErrors>

    RemoteOnlysignifica que na rede local você verá erros reais (muito útil durante o desenvolvimento). Também podemos reescrever a página de erro para qualquer código de erro.

  2. Defina o parâmetro de resposta mágica e o código de status de resposta (no módulo de tratamento de erros ou no atributo de tratamento de erros)

      HttpContext.Current.Response.StatusCode = 500;
      HttpContext.Current.Response.TrySkipIisCustomErrors = true;
  3. Defina outra configuração mágica na configuração da web na system.webServerseção:

    <httpErrors errorMode="Detailed" />

Esta foi a última coisa que encontrei e depois disso posso ver erros personalizados no servidor de produção.

Andrew Orsich
fonte
8
Esta ainda não é a solução ideal. Ele não fornece códigos de resposta HTTP adequados. Usando customErrorsresultados em um redirecionamento 302 apenas para carregar sua página de erro.
Justin Helgerson
@ Ek0nomik Eu nunca vi soluções ideais :)
Andrew Orsich
2
@JustinHelgerson Eu configurei um ErrorController para cada código de status e defini os Códigos de Resposta dentro dessas ações e ele definiu os códigos de resposta bem. Eu testei com o Fiddler e os códigos de status 404 respondem com um 404 e as páginas com 500 erros respondem com 500. E em ambos os casos, minhas visualizações de erro personalizadas são exibidas
Shane Neuville
meu colega de equipe acabou de excluir a exibição de erro na pasta compartilhada , para fazer um erro de tempo de execução com o código de status 500 para redirecionar para a página de erro personalizada, conforme mencionado aqui e aqui . A esperança ajuda alguém.
shaijut
41

Eu fiz meu tratamento de erros funcionar criando um ErrorController que retorna as visualizações neste artigo. Também tive que adicionar "Catch All" à rota em global.asax.

Não consigo ver como ele chegará a qualquer uma dessas páginas de erro se não estiver no Web.config ..? Meu Web.config precisava especificar:

customErrors mode="On" defaultRedirect="~/Error/Unknown"

e também adicionei:

error statusCode="404" redirect="~/Error/NotFound"
Jack Smit
fonte
2
Obrigado - isso me ajudou, 2 anos depois! O que você faz se quiser apenas um erro personalizado para 404, nada mais?
Shaul Behr
1
@Shaul outro ano depois ... não defina o defaultRedirect, exclua o atributo completamente.
NJE
27

Fonte

NotFoundMVC - Fornece uma página 404 amigável sempre que um controlador, ação ou rota não é encontrado em seu aplicativo ASP.NET MVC3. Uma exibição chamada NotFound é renderizada em vez da página de erro ASP.NET padrão.

Você pode adicionar este plug-in via nuget usando: Install-Package NotFoundMvc

NotFoundMvc se instala automaticamente durante a inicialização do aplicativo da web. Ele lida com todas as maneiras diferentes que uma 404 HttpException normalmente é lançada pela ASP.NET MVC. Isso inclui um controlador, ação e rota ausentes.

Guia de instalação passo a passo:

1 - Clique com o botão direito em seu projeto e selecione Gerenciar pacotes de Nuget ...

2 - Procure NotFoundMvce instale-o. insira a descrição da imagem aqui

3 - Assim que a instalação for concluída, dois arquivos serão adicionados ao seu projeto. Conforme mostrado nas imagens abaixo.

insira a descrição da imagem aqui

4 - Abra o novo NotFound.cshtml adicionado presente em Views / Shared e modifique-o à sua vontade. Agora execute o aplicativo e digite um url incorreto, e você será saudado com uma página 404 amigável.

insira a descrição da imagem aqui

Não mais, os usuários receberão mensagens de erro como Server Error in '/' Application. The resource cannot be found.

Espero que isto ajude :)

PS: Parabéns a Andrew Davey por fazer um plugin tão incrível.

Yasser Shaikh
fonte
@niico Olhando para o código-fonte, parece definir o código de resposta para 404, então sim.
Will Dean
20

Tente isso em web.config para substituir as páginas de erro do IIS. Essa é a melhor solução, eu acho, e também envia o código de status correto.

<system.webServer>
  <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>
</system.webServer>

Mais informações de Tipila - Use páginas de erro personalizadas ASP.NET MVC

Amila
fonte
1
+1. Esta é a única coisa que parecia funcionar de forma consistente para mim no MVC5 com várias áreas.
hawkke
Esta é a solução mais fácil. Porém, você não pode usar a sintaxe do razor na sua página de erro.
highace
15

Esta solução não precisa de alterações no arquivo web.config ou rotas pega-tudo.

Primeiro, crie um controlador como este;

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Title = "Regular Error";
        return View();
    }

    public ActionResult NotFound404()
    {
        ViewBag.Title = "Error 404 - File not Found";
        return View("Index");
    }
}

Em seguida, crie a visualização em "Views / Error / Index.cshtml" como;

 @{
      Layout = "~/Views/Shared/_Layout.cshtml";
  }                     
  <p>We're sorry, page you're looking for is, sadly, not here.</p>

Em seguida, adicione o seguinte no arquivo asax global conforme abaixo:

protected void Application_Error(object sender, EventArgs e)
{
        // Do whatever you want to do with the error

        //Show the custom error page...
        Server.ClearError(); 
        var routeData = new RouteData();
        routeData.Values["controller"] = "Error";

        if ((Context.Server.GetLastError() is HttpException) && ((Context.Server.GetLastError() as HttpException).GetHttpCode() != 404))
        {
            routeData.Values["action"] = "Index";
        }
        else
        {
            // Handle 404 error and response code
            Response.StatusCode = 404;
            routeData.Values["action"] = "NotFound404";
        } 
        Response.TrySkipIisCustomErrors = true; // If you are using IIS7, have this line
        IController errorsController = new ErrorController();
        HttpContextWrapper wrapper = new HttpContextWrapper(Context);
        var rc = new System.Web.Routing.RequestContext(wrapper, routeData);
        errorsController.Execute(rc);

        Response.End();
}

Se você ainda obtiver a página de erro personalizada do IIS após fazer isso, certifique-se de que as seções a seguir estejam comentadas (ou vazias) no arquivo de configuração da web:

<system.web>
   <customErrors mode="Off" />
</system.web>
<system.webServer>   
   <httpErrors>     
   </httpErrors>
</system.webServer>
Sujeewa
fonte
Não acho que essa abordagem funcione em todas as instâncias, uma vez que um 404 é frequentemente interceptado pelo servidor da web e, portanto, nunca manipulado no nível do aplicativo.
Chris Halcrow
Quero acrescentar que, se você seguir essa abordagem, recomendo adicionar um Response.End();ao final de seu manipulador Application_Error. Sem isso, apenas a primeira ocorrência de um erro é disparada para uma determinada sessão, o que é um problema quando você tem, por exemplo, um registro de erro personalizado no manipulador Application_Error.
Keith
6

Se você trabalha no MVC 4, pode assistir a este solução, funcionou para mim.

Adicione o seguinte método Application_Error ao meu Global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Server.ClearError();

    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Error");
    routeData.Values.Add("action", "Index");
    routeData.Values.Add("exception", exception);

    if (exception.GetType() == typeof(HttpException))
    {
        routeData.Values.Add("statusCode", ((HttpException)exception).GetHttpCode());
    }
    else
    {
        routeData.Values.Add("statusCode", 500);
    }

    IController controller = new ErrorController();
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    Response.End();

O controlador em si é muito simples:

public class ErrorController : Controller
{
    public ActionResult Index(int statusCode, Exception exception)
    {
        Response.StatusCode = statusCode;
        return View();
    }
}

Verifique o código-fonte completo de Mvc4CustomErrorPage no GitHub .

Duc Hoang
fonte
0

Eu tive o mesmo problema, o que você deve fazer é, em vez de adicionar o atributo customErrors no arquivo web.config na sua pasta Views, você deve adicioná-lo no arquivo web.config na pasta raiz do seu projeto

Fume isto
fonte
0

Aqui está a resposta verdadeira que permite personalizar totalmente a página de erro em um único lugar. Não há necessidade de modificar web.config ou criar código separado.

Funciona também em MVC 5.

Adicione este código ao controlador:

        if (bad) {
            Response.Clear();
            Response.TrySkipIisCustomErrors = true;
            Response.Write(product + I(" Toodet pole"));
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            //Response.ContentType = "text/html; charset=utf-8";
            Response.End();
            return null;
        }

Baseado em http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages

Andrus
fonte
0

Vou falar sobre alguns casos específicos,

se você estiver usando o 'método PageNotFound' no HomeController como abaixo

[Route("~/404")]
public ActionResult PageNotFound()
{
  return MyView();
}

não funcionaria isso. Mas você deve limpar as tags de rota como abaixo,

//[Route("~/404")]
public ActionResult PageNotFound()
{
  return MyView();
}

E se você alterá-lo como o nome do método em web.config, ele funciona. No entanto, não se esqueça de fazer o código abaixo em web.config

<customErrors mode="On">
  <error statusCode="404" redirect="~/PageNotFound" /> 
 *// it is not "~/404" because it is not accepted url in Route Tag like [Route("404")]*
</customErrors>
Furkan Ozturk
fonte