I am using custom errors (web.config
) to send unhandled exceptions to my ErrorController
.
In my main Controller I throw an exception, catch it in a catch
block and set the TempData
to have some user-friendly message. I then throw
which is then sent to the ErrorController
.
When I check the TempData
here, it is empty (as is the Session
but it would be, wouldn't it?).
I am sure it is not meant to be empty, and this is the right way of sending data between controllers... but it is not working! Am I mistaken in my understanding?
HomeController
:
catch (Exception ex)
{
TempData["MsgForUser"] = "useful message to show";
// Do some logging...
throw;
}
ErrorController
:
public ActionResult DisplayError(int id)
{
ViewBag.MsgForUser = TempData["MsgForUser"];
return View();
}
Web.config
:
<system.web>
<customErrors mode="On" defaultRedirect="~/ErrorPage/DisplayError">
<error redirect="~/Error/DisplayError/403" statusCode="403" />
<error redirect="~/Error/DisplayError/404" statusCode="404" />
<error redirect="~/Error/DisplayError/500" statusCode="500" />
</customErrors>
I found that the session was being destroyed upon doing a throw
in the top-level catch
block, because the request to the error controller.
However I did find the returning RedirectToAction()
preserved it.
So, I created my own exception type:
public class MyApplicationException : Exception
{
/// <summary>
/// A message that can be shown to the user i.e. not technical
/// </summary>
public string MessageForUser { get; set; }
public MyApplicationException()
{
}
public MyApplicationException(string message, string messageForUser="")
: base(message)
{
MessageForUser = messageForUser;
}
public MyApplicationException(string message, Exception inner, string messageForUser = "")
: base(message, inner)
{
MessageForUser = messageForUser;
}
}
then when I encounter a problem that I am expecting and will end up sending the user to a generic error page, I use it like so:
if (MyList.Count > 14)
{
throw new MyApplicationException("More than 14 records received: " + MyList.Count.ToString(), "There is a maximum limit of 14 records that can be processed at a time. Please call us to discuss your requirement to process in greater quantities.");
}
and upon catching it I pick out the message for the user, log the error, and return RedirectToAction()
to go to the error page without losing the TempData
.
catch (MyApplicationException myAppEx)
{
// If there is a user-friendly message then store it for DisplayError to show
if (String.IsNullOrEmpty(myAppEx.MessageForUser) == false)
{
TempData["MsgForUser"] = myAppEx.MessageForUser;
}
// Do some logging to elmah or a custom logger
util.LogError(myAppEx, "MyFunc() threw an exception", args);
//
// We cannot throw here because we lose the Session & TempData. we have to do a manual redirect.
//
return RedirectToAction("DisplayError", "ErrorPage", new { id = 500 });
}
and finally in my view, I look for it and display if it exists:
<p>
An error occurred. Please check & try again later.
</p>
@if (String.IsNullOrEmpty(ViewBag.MsgForUser) == false)
{
<p class="more-info">Details: @ViewBag.MsgForUser</p>
}