How to return to the previous page by Handleerrorattribute?

Asked

Viewed 673 times

1

I am 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 treat so that validation errors of Entityframwork, which have gone through the verification of the ModelState in Actions (if (ModelState.IsValid)), are added in the Modelstate.

Otherwise I want to play the bug on TempData.

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

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

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

How can I redirect to the previous page?

2 answers

1

This answer proposed to follow a path suggested by the author of the question, which is not correct for the case. Validation errors should be treated differently from application errors, because application errors do not allow the View earlier is returned in the same way. An application error needs to display error details because ASP.NET MVC understands that these errors are not predicted by the developer.

For teaching purposes, I will keep the answer here because it may be useful for other examples using HandleErrorAttribute.

I would make a filter that registers 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;
        }
}

Globally register the filter:

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 the 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);
    }
}
  • I think the problem is not so much with picking up the url, I even put it manually, although I saw that its example is working correctly (I just needed to implement Iactionfilter instead of Iresultfilter - I don’t know why). And I’m still being redirected to the standard IIS error page.

  • There is a View standard for errors. I can still improve the answer. If it’s of interest to you, add.

  • I would like to! I really need to go back to the previous View.

  • I mean, I want to go back to the View that made the request. So I can show the model errors in the ValidationSummary or the custom error message (alert).

  • 1

    So, then I guess you’re going the wrong way. HandleErrorAttribute does not serve to treat validation, because a validation error is different from an application error. An application error cannot produce the same View that a normal flow. It asks for a View just for him. If you want, I can produce a second answer with the right path.

  • Please, I would like yes in the right way! Thank you very much!

Show 1 more comment

1


The correct way to treat entity validation errors from context 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();
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.