How to return to the previous page by the HandleErrorAttribute?

1

I'm trying to implement a global filter for error handling and started testing as follows:

public class MyHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        var exception = filterContext.Exception;
        var controller = ((Controller)filterContext.Controller);

        if (exception is DbEntityValidationException)
        {
            var dbEx = exception as DbEntityValidationException;
            foreach (var ve in dbEx.EntityValidationErrors.SelectMany(x => x.ValidationErrors))
                controller.ModelState.AddModelError(string.Empty, ve.ErrorMessage);
        }
        else
        {
            controller.TempData["ErrorMessage"] = exception.GetBaseException().Message;
        }

        var routeData = filterContext.RouteData;
        var currentController = routeData.GetRequiredString("action");
        var currentAction = routeData.GetRequiredString("controller");
        filterContext.Result = new RedirectResult($"/{currentController}/{currentAction}");
    }
}

At first, I want to see that EntityFramwork validation errors, which have passed the ModelState checks on Actions ( if (ModelState.IsValid) ), are added in ModelState.

Otherwise I want to throw the error in TempData .

For both I want the user to be redirected to the page from which he made the request, but I can not even set the Result of the filterContext : filterContext.Result = new RedirectResult($"/{currentController}/{currentAction}");

The filter is registered in FilterCondif.cs and I can debug it.

From TempData I check for an error message and then present a custom message.

How do I redirect to the previous page?

    
asked by anonymous 24.09.2015 / 15:57

2 answers

1

The correct way to handle context validation errors is by Controller :

public ActionResult MinhaAction(MeuModel model)
{
    try
    {
        db.PersonalDetails.Add(model);
        db.SaveChanges();
    }
    catch (DbEntityValidationException ee)
    {
        foreach (var error in ee.EntityValidationErrors)
        {
            foreach(var thisError in error.ValidationErrors)
            {
                ModelState.AddModelError(string.Empty, thisError);
            }                    
        }
    }

    return View();
}

If you want this behavior to be common, you can do the following:

public abstract class Controller : System.Web.Mvc.Controller
{
    protected MeuProjetoContext db = new MeuProjetoContext();

    protected virtual boolean PersistirOuFalhar() 
    {
        try
        {
            db.SaveChanges();
        }
        catch (DbEntityValidationException ee)
        {
            foreach (var error in ee.EntityValidationErrors)
            {
                foreach(var thisError in error.ValidationErrors)
                {
                    ModelState.AddModelError(string.Empty, thisError);
                }                    
            }

            return false;
        }

        return true;
    }
}

MinhaAction would look like this:

public ActionResult MinhaAction(MeuModel model)
{
    db.PersonalDetails.Add(model);
    var resultado = PersistirOuFalhar();

    // Aí você usa resultado da maneira que quiser. 
    // Ou pode simplesmente devolver a View.
    // O ValidationSummary estará preenchido.
    return View();
}
    
24.09.2015 / 17:11
1
  

This response was intended to follow a path suggested by the questioner, which is not correct for the case. Validation errors should be handled differently from application errors, because application errors do not allow the previous View to be returned in the same way. An application error needs to display error details because ASP.NET MVC understands that these errors are not anticipated by the developer.

     

For teaching purposes, I'll keep the answer here because it can be useful for other examples using HandleErrorAttribute .

I would do a filter that records the previous URL:

~ / Attributes / UrlAnteriorAttribute.cs

public class UrlAnteriorAttribute : FilterAttribute, IResultFilter
{
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
        }
        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            filterContext.Controller.ViewBag.UrlAnterior = filterContext.HttpContext.Request.UrlReferrer;
        }
}

Register the filter globally:

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalFilters.Filters.Add(new UrlAnteriorAttribute());
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        ...
    }
}

Within HandleError :

public class MyHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        var exception = filterContext.Exception;
        var controller = ((Controller)filterContext.Controller);

        if (exception is DbEntityValidationException)
        {
            var dbEx = exception as DbEntityValidationException;
            foreach (var ve in dbEx.EntityValidationErrors.SelectMany(x => x.ValidationErrors))
                controller.ModelState.AddModelError(string.Empty, ve.ErrorMessage);
        }
        else
        {
            controller.TempData["ErrorMessage"] = exception.GetBaseException().Message;
        }

        var routeData = filterContext.RouteData;
        var currentController = routeData.GetRequiredString("action");
        var currentAction = routeData.GetRequiredString("controller");
        filterContext.Result = new RedirectResult(filterContext.Controller.ViewBag.UrlAnterior);
    }
}
    
24.09.2015 / 16:24