HandleError ASP.NET MVC

110

Comment utiliser le [HandleError]filtre dans asp.net MVC Preview 5?
J'ai défini les customErrors dans mon fichier Web.config

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

et mettez [HandleError] au-dessus de ma classe de contrôleur comme ceci:

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

Ensuite, je laisse mes contrôleurs hériter de cette classe et j'appelle CrashTest () sur eux. Visual Studio s'arrête à l'erreur et après avoir appuyé sur f5 pour continuer, je suis redirigé vers Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (où sxi est le nom du contrôleur utilisé. Bien sûr, le chemin ne peut pas être trouvé et je reçois "Erreur de serveur dans l'application '/'." 404.

Ce site a été porté de l'aperçu 3 à 5. Tout fonctionne (ce n'était pas beaucoup de travail à porter) sauf la gestion des erreurs. Lorsque je crée un nouveau projet complet, la gestion des erreurs semble fonctionner.

Des idées?

--Remarque--
Puisque cette question a maintenant plus de 3K vues, j'ai pensé qu'il serait avantageux de mettre ce que j'utilise actuellement (ASP.NET MVC 1.0). Dans le projet mvc contrib, il y a un attribut génial appelé "RescueAttribute". Vous devriez probablement le vérifier aussi;)

Boris Callens
la source
Lien vers la RescueAttributesource: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Peter

Réponses:

158
[HandleError]

Lorsque vous ne fournissez que l'attribut HandleError à votre classe (ou à votre méthode d'action d'ailleurs), alors lorsqu'une exception non gérée se produit, MVC recherche d'abord une vue correspondante nommée "Erreur" dans le dossier Vue du contrôleur. S'il ne peut pas le trouver là-bas, il procédera à la recherche dans le dossier Shared View (qui devrait contenir un fichier Error.aspx par défaut)

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

Vous pouvez également empiler des attributs supplémentaires avec des informations spécifiques sur le type d'exception que vous recherchez. À ce stade, vous pouvez diriger l'erreur vers une vue spécifique autre que la vue "Erreur" par défaut.

Pour plus d'informations, consultez l'article de blog de Scott Guthrie à ce sujet.

Manoir Elijah
la source
1
Merci pour les informations détaillées. Je ne sais pas ce que j'ai fait de mal, mais j'ai créé un nouveau projet, porté toutes les vues, contrôleurs et modèles existants et maintenant cela fonctionne. Je ne savais pas pour les vues sélectives cependant.
Boris Callens
Si la journalisation de ces exceptions est souhaitée, serait-ce un endroit acceptable pour ajouter un code derrière la vue?
Peter J
6
Iconique, voici ma réponse "mieux vaut tard que jamais" à votre commentaire: Vous pouvez à la place sous-classer le HandleErrorAttribute et remplacer sa méthode "OnException": Ensuite, insérez les actions de journalisation ou personnalisées que vous désirez. Vous pouvez alors soit gérer entièrement l'exception (en définissant context.ExceptionHandled sur true), soit revenir à la méthode OnException de la classe de base pour cela. Voici un excellent article qui peut vous aider: blog.dantup.me.uk/2009/04/…
Funka
J'ai beaucoup de contrôleurs, puis-je gérer cela à l'intérieur global.asaxcomme celui-ci pour afficher un message aux utilisateurs?
shaijut
@ Pourquoi ne pas utiliser la même page d'erreur que PartialView et la rendre dans la boîte de dialogue modale après une exception? Pouvez-vous donner un exemple dans votre réponse? Ce que je veux réaliser a été expliqué sur la gestion des erreurs globales à l'aide de PartialView dans MVC .
Jack le
23

Il convient également de noter que les erreurs qui ne définissent pas le code d'erreur http sur 500

(par exemple, UnauthorizedAccessException)

ne sera pas géré par le filtre HandleError.

Corin Blaikie
la source
1
C'est vrai, mais consultez le RescueAttribute dans MVC contrib (lien dans OP)
Boris Callens
14

Solution pour le code d'erreur http à 500, il s'agit d'un attribut appelé [ERROR] le mettre sur une action

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

}

//EXEMPLE:

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

Les attributs dans MVC sont très utiles dans la gestion des erreurs dans les méthodes get et post , ils suivent également les appels ajax .

Créez un contrôleur de base dans votre application et héritez-en dans votre contrôleur principal (EmployeeController).

Public class EmployeeController: BaseController

Ajoutez le code ci-dessous dans le contrôleur 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");
    }

}

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

Recherche le répertoire: Root / App_Start / FilterConfig.cs

Ajoutez le code ci-dessous:

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

Suivre l'erreur AJAX:

Appelez la fonction CheckAJAXError lors du chargement de la page de mise en page.

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 - Développeur Full Stack
la source
Vous divulguez des détails d'exception sur les requêtes AJAX, vous ne le voulez pas toujours. Votre code de journalisation n'est pas thread-safe. Vous supposez qu'il y a une vue d'erreur et vous la renvoyez, sans modifier le code de réponse. Ensuite, vous allez vérifier certaines chaînes d'erreur en JavaScript (qu'en est-il de la localisation?). En gros, vous répétez ce qu'une réponse existante dit déjà: "Remplacer OnExceptionpour gérer les exceptions" , mais en montrant une mise en œuvre plutôt mauvaise.
CodeCaster le
Qu'est-ce que le paramètre @ School.Resource.Messages.ReferanceExist?
Jack le
@CodeCaster Connaissez-vous un meilleur moyen d'utiliser un tel type de méthode de gestion des erreurs avec AJAX dans ASP.NET MVC? Une aide s'il vous plaît?
Jack le
Renvoyez un 400 ou 500, comme HTTP est prévu. N'allez pas chercher des chaînes spécifiques dans le corps de la réponse.
CodeCaster
@CodeCaster Pourriez-vous jeter un œil à la gestion des erreurs globales à l'aide de PartialView dans MVC concernant ce problème?
Jack
4

Il vous manque Error.aspx :) Dans l'aperçu 5, il se trouve dans votre dossier Views / Shared. Copiez-le simplement à partir d'un nouveau projet Preview 5.

Ricky
la source
Merci pour la réponse, mais j'ai déjà copié la page Error.aspx. Cela aurait pu être quelque chose que j'oublierais normalement, mais pas cette fois. : 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
la source