I have been capturing exceptions and redirecting users to an error page. I pass the exception message and a return URL to tell the user what happened and allow them to return another page.
try
{
return action(parameters);
}
catch (Exception exception)
{
ErrorViewModel errorModel = new ErrorViewModel();
errorModel.ErrorMessage = "An error occured while doing something.";
errorModel.ErrorDetails = exception.Message;
errorModel.ReturnUrl = Url.Action("Controller", "Action");
return RedirectToAction("Index", "Error", errorModel);
}
This seems like way too much code to wrap around every action. I am using a global filter for errors:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
HandleErrorAttribute attribute = new HandleErrorAttribute();
filters.Add(attribute);
}
and I have my web.config setup like this:
<customErrors mode="On" defaultRedirect="~/Error/Unknown">
But, this only works for unhandled exceptions.
I want exceptions to cause a redirect to an error controller/action taking a parameter holding the exception details. It would be nice if I could indicate the return URL on a action-by-action basis, or to have a default if none is provided.
Here is a little RedirectOnErrorAttribute class I created:
using System;
using System.Web.Mvc;
using MyApp.Web.Models;
namespace MyApp.Web.Utilities
{
public class RedirectOnErrorAttribute : ActionFilterAttribute
{
/// <summary>
/// Initializes a new instance of a RedirectOnErrorAttribute.
/// </summary>
public RedirectOnErrorAttribute()
{
ErrorMessage = "An error occurred while processing your request.";
}
/// <summary>
/// Gets or sets the controller to redirect to.
/// </summary>
public string ReturnController { get; set; }
/// <summary>
/// Gets or sets the action to redirect to.
/// </summary>
public string ReturnAction { get; set; }
/// <summary>
/// Gets or sets the error message.
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Redirects the user to an error screen if an exception is thrown.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null && !filterContext.ExceptionHandled)
{
ErrorViewModel viewModel = new ErrorViewModel();
viewModel.ErrorMessage = ErrorMessage;
viewModel.ErrorDetails = filterContext.Exception.Message;
string controller = ReturnController;
string action = ReturnAction;
if (String.IsNullOrWhiteSpace(controller))
{
controller = (string)filterContext.RequestContext.RouteData.Values["controller"];
}
if (String.IsNullOrWhiteSpace(action))
{
action = "Index";
}
UrlHelper helper = new UrlHelper(filterContext.RequestContext);
viewModel.ReturnUrl = helper.Action(action, controller);
string url = helper.Action("Index", "Error", viewModel);
filterContext.Result = new RedirectResult(url);
filterContext.ExceptionHandled = true;
}
base.OnActionExecuted(filterContext);
}
}
}
Now, I can decorate all of my actions like this:
[RedirectOnError(ErrorMessage="An error occurred while doing something.", ReturnController="Controller", ReturnAction="Action")]