ASP.NET MVC HandleError

110

Como faço para fazer o [HandleError]filtro no asp.net MVC Preview 5?
Eu defino os customErrors em meu arquivo Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">
  <error statusCode="403" redirect="NoAccess.htm"/>
  <error statusCode="404" redirect="FileNotFound.htm"/>
</customErrors>

e coloque [HandleError] acima da minha classe de controlador assim:

[HandleError]
public class DSWebsiteController: Controller
{
    [snip]
    public ActionResult CrashTest()
    {
        throw new Exception("Oh Noes!");
    }
}

Então eu deixo meus controladores herdarem desta classe e chamar CrashTest () neles. Visual Studio para no erro e depois de pressionar f5 para continuar, sou redirecionado para Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (onde sxi é o nome do controlador usado. Claro que o caminho não pode ser encontrado e eu obtenho "Erro de servidor no aplicativo '/'." 404.

Este site foi portado da pré-visualização 3 para 5. Tudo funciona (não deu muito trabalho para portar), exceto o tratamento de erros. Quando crio um novo projeto completo, o tratamento de erros parece funcionar.

Ideias?

--Observação--
Já que esta pergunta tem mais de 3 mil visualizações agora, pensei que seria benéfico colocar o que estou usando atualmente (ASP.NET MVC 1.0). No projeto mvc contrib, há um atributo brilhante chamado "RescueAttribute". Você provavelmente também deve dar uma olhada;)

Boris Callens
fonte
Link para a RescueAttributefonte: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Peter

Respostas:

158
[HandleError]

Quando você fornece apenas o atributo HandleError para sua classe (ou para seu método de ação para esse assunto), então, quando uma exceção não tratada ocorrer, o MVC irá procurar por uma Visualização correspondente chamada "Erro" primeiro na pasta Visualização do Controlador. Se não conseguir encontrá-lo lá, ele continuará a procurar na pasta Shared View (que deve ter um arquivo Error.aspx por padrão)

[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "LameErrorHandling")]

Você também pode empilhar atributos adicionais com informações específicas sobre o tipo de exceção que está procurando. Nesse ponto, você pode direcionar o erro para uma visualização específica diferente da visualização padrão "Erro".

Para obter mais informações, dê uma olhada na postagem do blog de Scott Guthrie sobre isso.

Elijah Manor
fonte
1
Obrigado pelas informações estendidas. Não sei o que fiz de errado, mas criei um novo projeto, portei todas as visualizações, controladores e modelos existentes nele e agora funciona. Não sabia sobre as visualizações seletivas.
Boris Callens,
Se o registro dessas exceções for desejado, este seria um local aceitável para adicionar um code-behind à visualização?
Peter J
6
Icônico, aqui está minha resposta "antes tarde do que nunca" ao seu comentário: Você pode, em vez disso, criar uma subclasse de HandleErrorAttribute e substituir seu método "OnException": Em seguida, insira qualquer registro ou ações personalizadas que desejar. Você pode então tratar totalmente a exceção (configurando context.ExceptionHandled como true) ou adiar de volta para o método OnException da própria classe base para isso. Aqui está um excelente artigo que pode ajudar com isso: blog.dantup.me.uk/2009/04/…
Funka
Eu tenho muita controladores para que eu possa lidar com isso dentro global.asaxcomo este para mostrar uma mensagem aos usuários?
shaijut de
@ Que tal usar a mesma página de erro que PartialView e renderizá-la na caixa de diálogo modal após a exceção ocorrer? Você poderia dar um exemplo em sua resposta? O que desejo alcançar foi explicado no tratamento de erros globais usando PartialView no MVC .
Jack de
23

Também deve ser observado que erros que não definem o código de erro http para 500

(por exemplo, UnauthorizedAccessException)

não será gerenciado pelo filtro HandleError.

Corin Blaikie
fonte
1
Verdade, mas verifique o RescueAttribute em MVC contrib (link em OP)
Boris Callens
14

Solução para código de erro http para 500 este é um atributo chamado [ERROR] coloque-o em uma ação

public class Error: System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {

            if (filterContext.HttpContext.IsCustomErrorEnabled)
            {
                filterContext.ExceptionHandled = true;

            }
            base.OnException(filterContext);
            //OVERRIDE THE 500 ERROR  
           filterContext.HttpContext.Response.StatusCode = 200;
    }

    private static void RaiseErrorSignal(Exception e)
    {
        var context = HttpContext.Current;
      // using.Elmah.ErrorSignal.FromContext(context).Raise(e, context);
    } 

}

//EXEMPLO:

[Error]
[HandleError]
[PopulateSiteMap(SiteMapName="Mifel1", ViewDataKey="Mifel1")]
public class ApplicationController : Controller
{
}
Raul
fonte
12

Atributos em MVC são muito úteis no tratamento de erros nos métodos get e post , e também rastreiam uma chamada ajax .

Crie um controlador de base em sua aplicação e herde-o em seu controlador principal (EmployeeController).

public class EmployeeController: BaseController

Adicione o código abaixo no controlador de base.

/// <summary>
/// Base Controller
/// </summary>
public class BaseController : Controller
{       
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception ex = filterContext.Exception;

        //Save error log in file
        if (ConfigurationManager.AppSettings["SaveErrorLog"].ToString().Trim().ToUpper() == "TRUE")
        {
            SaveErrorLog(ex, filterContext);
        }

        // if the request is AJAX return JSON else view.
        if (IsAjax(filterContext))
        {
            //Because its a exception raised after ajax invocation
            //Lets return Json
            filterContext.Result = new JsonResult()
            {
                Data = Convert.ToString(filterContext.Exception),
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();

            filterContext.Result = new ViewResult()
            {
                //Error page to load
                ViewName = "Error",
                ViewData = new ViewDataDictionary()
            };

            base.OnException(filterContext);
        }
    }

    /// <summary>
    /// Determines whether the specified filter context is ajax.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    private bool IsAjax(ExceptionContext filterContext)
    {
        return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
    }

    /// <summary>
    /// Saves the error log.
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <param name="filterContext">The filter context.</param>
    void SaveErrorLog(Exception ex, ExceptionContext filterContext)
    {
        string logMessage = ex.ToString();

        string logDirectory = Server.MapPath(Url.Content("~/ErrorLog/"));

        DateTime currentDateTime = DateTime.Now;
        string currentDateTimeString = currentDateTime.ToString();
        CheckCreateLogDirectory(logDirectory);
        string logLine = BuildLogLine(currentDateTime, logMessage, filterContext);
        logDirectory = (logDirectory + "\\Log_" + LogFileName(DateTime.Now) + ".txt");

        StreamWriter streamWriter = null;
        try
        {
            streamWriter = new StreamWriter(logDirectory, true);
            streamWriter.WriteLine(logLine);
        }
        catch
        {
        }
        finally
        {
            if (streamWriter != null)
            {
                streamWriter.Close();
            }
        }
    }

    /// <summary>
    /// Checks the create log directory.
    /// </summary>
    /// <param name="logPath">The log path.</param>
    bool CheckCreateLogDirectory(string logPath)
    {
        bool loggingDirectoryExists = false;
        DirectoryInfo directoryInfo = new DirectoryInfo(logPath);
        if (directoryInfo.Exists)
        {
            loggingDirectoryExists = true;
        }
        else
        {
            try
            {
                Directory.CreateDirectory(logPath);
                loggingDirectoryExists = true;
            }
            catch
            {
            }
        }

        return loggingDirectoryExists;
    }

    /// <summary>
    /// Builds the log line.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    /// <param name="logMessage">The log message.</param>
    /// <param name="filterContext">The filter context.</param>       
    string BuildLogLine(DateTime currentDateTime, string logMessage, ExceptionContext filterContext)
    {
        string controllerName = filterContext.RouteData.Values["Controller"].ToString();
        string actionName = filterContext.RouteData.Values["Action"].ToString();

        RouteValueDictionary paramList = ((System.Web.Routing.Route)(filterContext.RouteData.Route)).Defaults;
        if (paramList != null)
        {
            paramList.Remove("Controller");
            paramList.Remove("Action");
        }

        StringBuilder loglineStringBuilder = new StringBuilder();

        loglineStringBuilder.Append("Log Time : ");
        loglineStringBuilder.Append(LogFileEntryDateTime(currentDateTime));
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("Username : ");
        loglineStringBuilder.Append(Session["LogedInUserName"]);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ControllerName : ");
        loglineStringBuilder.Append(controllerName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ActionName : ");
        loglineStringBuilder.Append(actionName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("----------------------------------------------------------------------------------------------------------");
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append(logMessage);
        loglineStringBuilder.Append(System.Environment.NewLine);
        loglineStringBuilder.Append("==========================================================================================================");

        return loglineStringBuilder.ToString();
    }

    /// <summary>
    /// Logs the file entry date time.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileEntryDateTime(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd-MMM-yyyy HH:mm:ss");
    }

    /// <summary>
    /// Logs the name of the file.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileName(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd_MMM_yyyy");
    }

}

======================================================

Encontra o diretório: Root / App_Start / FilterConfig.cs

Adicione o código abaixo:

/// <summary>
/// Filter Config
/// </summary>
public class FilterConfig
{
    /// <summary>
    /// Registers the global filters.
    /// </summary>
    /// <param name="filters">The filters.</param>
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

Rastrear erro AJAX:

Chame a função CheckAJAXError no carregamento da página de layout.

function CheckAJAXError() {
    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {

        var ex;
        if (String(thrownError).toUpperCase() == "LOGIN") {
            var url = '@Url.Action("Login", "Login")';
            window.location = url;
        }
        else if (String(jqXHR.responseText).toUpperCase().indexOf("THE DELETE STATEMENT CONFLICTED WITH THE REFERENCE CONSTRAINT") >= 0) {

            toastr.error('ReferanceExistMessage');
        }
        else if (String(thrownError).toUpperCase() == "INTERNAL SERVER ERROR") {
            ex = ajaxSettings.url;
            //var url = '@Url.Action("ErrorLog", "Home")?exurl=' + ex;
            var url = '@Url.Action("ErrorLog", "Home")';
            window.location = url;
        }
    });
};
Sandip - Desenvolvedor Full Stack
fonte
Você está vazando detalhes de exceção em solicitações AJAX, você nem sempre quer isso. Seu código de registro não é seguro para thread. Você está assumindo que há uma visualização Error e retornando-a, sem alterar o código de resposta. Então você vai verificar se há certas strings de erro em JavaScript (e a localização?). Basicamente, você está repetindo o que uma resposta existente já diz: "Substituir OnExceptionpara lidar com exceções" , mas mostrando uma implementação muito ruim disso.
CodeCaster
O que é o parâmetro @ School.Resource.Messages.ReferanceExist?
Jack de
@CodeCaster Você conhece alguma maneira melhor de usar esse tipo de método de tratamento de erros com AJAX na ASP.NET MVC? Alguma ajuda por favor?
Jack de
Retorne 400 ou 500, conforme o objetivo do HTTP. Não vá cavando para strings específicas no corpo da resposta.
CodeCaster de
@CodeCaster Você poderia dar uma olhada no tratamento de erros globais usando PartialView no MVC em relação a este problema?
Jack de
4

Está faltando Error.aspx :) Na visualização 5, ele está localizado na pasta Views / Shared. Basta copiá-lo de um novo projeto do Preview 5.

Ricky
fonte
Obrigado pela resposta, mas já copiei a página Error.aspx. Poderia realmente ter sido algo que eu normalmente esqueceria, mas não desta vez. : P
Boris Callens,
-1
    [HandleError]
    public class ErrorController : Controller
    {        
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult NotAuthorized()
        {
            //401
            Response.StatusCode = (int)HttpStatusCode.Unauthorized;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult Forbidden()
    {
        //403
        Response.StatusCode = (int)HttpStatusCode.Forbidden;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult NotFound()
    {
        //404
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ViewResult ServerError()
    {
        //500
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

}

Jack
fonte