Returning a View Using a Model from ActionFilterAttribute

When implementing error handling using the built-in validation helpers in a strongly typed view, you usually create a try / catch block inside the controller and return the view with the corresponding model as a parameter in the View() method:



Controller

 public class MessageController : Controller { [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Models.Entities.Message message) { try { // Insert model into database var dc = new DataContext(); dc.Messages.InsertOnSubmit(message); dc.SubmitChanges(); return RedirectToAction("List"); } catch { /* If insert fails, return a view with it corresponding model to enable validation helpers */ return View(message); } } } 



View

 <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Models.Entities.Message>" %> <%= Html.ValidationSummary("Fill out fields marked with *") %> <% using (Html.BeginForm()) { %> <div><%= Html.TextBox("MessageText") %></div> <div><%= Html.ValidationMessage("MessageText", "*") %></div> <% } %> 



I applied a simple error handler in the form of ActionFilterAttribute, which can either redirect to the general idea of โ€‹โ€‹the error, or redirect to the view that caused the exception, and let spring helper validators come to life.

This is what my ActionFilterAttribute looks like:

 public class ErrorLoggingAttribute : ActionFilterAttribute, IExceptionFilter { private Boolean _onErrorRedirectToGenericErrorView; /// <param name="onErrorRedirectToGenericErrorView"> /// True: redirect to a generic error view. /// False: redirect back the view which threw an exception /// </param> public ErrorLoggingAttribute(Boolean onErrorRedirectToGenericErrorView) { _onErrorRedirectToGenericErrorView = onErrorRedirectToGenericErrorView; } public void OnException(ExceptionContext ec) { if (_onErrorRedirectToGenericErrorView) { /* Redirect back to the view where the exception was thrown and include it model so the validation helpers will work */ } else { // Redirect to a generic error view ec.Result = new RedirectToRouteResult(new RouteValueDictionary { {"controller", "Error"}, {"action", "Index"} }); ec.ExceptionHandled = true; } } } 

Redirecting to a view that throws an exception is pretty simple. But here is the kicker: in order for the validation assistants to work, you need to provide an idea using this model.

How would you return the view that selected the exception and provide the view of the appropriate model? (In this case, Models.Entities.Message ).

+4
source share
4 answers

I got it for work!

For some odd reason, all I had to do was pass the ViewData to the new ResultView .

Here is the full code:

 public class ErrorLoggingAttribute : ActionFilterAttribute, IExceptionFilter { private String _controllerName, _actionName; private Boolean _redirectToGenericView = false; public ErrorLoggingAttribute() { } public ErrorLoggingAttribute(String actionName, String controllerName) { _controllerName = controllerName; _actionName = actionName; _redirectToGenericView = true; } void IExceptionFilter.OnException(ExceptionContext ec) { // log error if (_redirectToGenericView) { ec.Result = new RedirectToRouteResult(new RouteValueDictionary { {"controller", _controllerName}, {"action", _actionName} }); } else { ec.Result = new ViewResult { ViewName = ((RouteData) ec.RouteData).Values["action"].ToString(), TempData = ec.Controller.TempData, ViewData = ec.Controller.ViewData }; } ec.ExceptionHandled = true; } } 


Using


Here, how you should use the attribute on action-action, redirect to the same view (with its associated model) to enable standard validation helpers when an exception occurs:

 [ErrorLogging] [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Models.Entities.Message message) { var dc = new Models.DataContext(); dc.Messages.InsertOnSubmit(message); dc.SubmitChanges(); return RedirectToAction("List", new { id = message.MessageId }); } 

And here is how you will use this attribute to redirect to the general view when an exception occurs:

 [ErrorLogging("ControllerName", "ViewName")] [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Models.Entities.Message message) 


This is a complete separation of logic. Nothing in the controller except the base itself.

+5
source

Since you inherit ActionFilterAttribute From OnActionExecuting, you can capture your model.

  public override void OnActionExecuting(ActionExecutingContext filterContext) { var model = filterContext.Controller.ViewData.Model as YourModel; ... } 

But MVC already has HandleError installed, why don't you use this one and bake your own.

I suggest you read this blog on this issue.

+1
source

If your action throws an exception, there is no way to pass the model into the view, since the model has not yet been created or has not been completely created. This is probably why the result is zero. You cannot rely on data after an exception is thrown.

But you can pass the default model to your action filter as follows:

 [ErrorLogging(new EmptyModel())] // or to create using Activator [ErrorLogging(typeof(EmptyModel))] // or even set view name to be displayed [ErrorLogging("modelerror", new EmptyModel())] 

Thus, your filter will pass this โ€œerror modelโ€ that you explicitly configured to display when an error occurs.

0
source
 public class MessageController : Controller { public ActionResult Create() { return View(); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create( Message message ) { try { // Exceptions for flow control are so .NET 1.0 =) // ... your save code here } catch { // Ugly catch all error handler - do you really know you can fix the problem? What id the database server is dead!?! return View(); } } } 

Model details are already present in modelstate. Any errors should also be present in modelstate. Your exception handler should handle the case when you want to redirect to a common error page. It is better / more obvious to drop the attribute and if you want to redirect to catch, return the redirect result.

0
source

All Articles