1

In my ASP.NET MVC application I have an ErrorController, currently having only one action method that receives a custom class called Error and renders it to the view called Error.cshtml. For now, there're only 2 properties in the Error class:

public class Error{
   public string UserFriendlyMmessage{get;set;}
   public string ErrorMessage{get;set;}
}

The Error.cshtml view is strongly typed to the Error class. Whenever an exception is thrown inside an action method I do the following:

Error error = new Error { UserFriendlyText = "The friendly message", ErrorDescription = "Error to be logged" };
return RedirectToAction("Index", "Error", new System.Web.Routing.RouteValueDictionary(error));

And here's the action method of the ErrorController:

    public ActionResult Index(Error error)
    {
        return View(model: error, viewName:"Error");
    }

While this works well, this way all the model properties are shown in the url. Is there a better, more commonly used way of doing this?

6
  • When the error occurs, you could assign the Error to a TempData property, then return RedirectToAction("Index", "Error") and in the Index() method get the value from Tempdata and render the view. Commented Dec 11, 2014 at 12:25
  • Yeah, sure I can do that? But maybe there's definitely another approach? Do people even do the way I handle errors? Commented Dec 11, 2014 at 12:33
  • I've not seen an implementation like this (and I'm not sure I like it). Personally I have a BaseController decorated with a number of [HandleError()] attributes that define a custom exception type and the associated view to render, Then I might say throw an EditDeniedException("Some friendly message") and the framework automatically displays the associated view. Commented Dec 11, 2014 at 12:39
  • Oh, @StephenMuecke, I don't like it too. Could you please post this as an answer. A little code would make what you mean more clear. I appreciate it. Commented Dec 11, 2014 at 12:41
  • Its late and I need some sleep :) I'll add an answer with some code in the morning (although its not a direct answer to yoru question so I might delete it later) Commented Dec 11, 2014 at 12:45

3 Answers 3

5

Apart from the ugly query string, your at risk that you will exceed the query string limits, particularly if your messages are long and you add further properties to Error.

One option would be to persist the Error to a TempData property, then return RedirectToAction("Index", "Error") and in the Index() method get the value from TempData and render the view.

Another option would be to use the HandleErrorAttribute attribute to render views based on specific exception. For example

[HandleError(ExceptionType = typeof(MyException), View = "MyError")]
[HandleError(ExceptionType = typeof(AnotherException), View = "AnotherError")]
public class BaseController : Controller
{
}

and in the /Views/Shared folder, add specific error views (MyError.cshtml)

@model System.Web.Mvc.HandleErrorInfo
....
@Model.Exception.Message // display error message
...

Note HandleErrorInfo gives you access to the controller and action names as well as the exception details.

Then, in a specific method, if you throw an exception, the associated view will be displayed

public class MyController : BaseController
{
  public ActionResult SomeAction()
  {
    if(someCondition)
    {
      throw new MyException("Some friendly error message");
    }
    return View();
  }
}

Where MyException is an inbuilt exception, or you own custom exception that derives from Exception

Note you also need to set the following in web.config

<system.web>
  <customErrors mode="On" defaultRedirect="Error" />
</system.web>
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much. I've noticed you're always the first one to answer my questions on MVC
1

Is there any need for redirect to special error action while you are filling Error class in each action and will have the same view ? You may just do the following:

try{
   DoSomeStuff();
}
except (Exception e)
{
    Error error = new Error { UserFriendlyText = "The friendly message", ErrorDescription = "Error to be logged" };
    return View("Error", error);  //error view
}
return View(); //Default way

For ourself we have choosen another approach to show errors. There is no special view to show error. In this case user will loose context where error happened. Instead of special view we created the following structure.

  1. We have BaseController with couple of useful functions:

    protected void AddErrorMessage(String errorMessage)
    {
        var errorMessages = TempData["ErrorMessages"] as List<String>;
        if (errorMessages == null)
            errorMessages = new List<String>();
        errorMessages.Add(errorMessage);
        TempData["ErrorMessages"] = errorMessages;
    }
    
    protected void AddSuccessMessage(String successMessage)
    {
        var successMessages = TempData["SuccessMessages"] as List<String>;
        if (successMessages == null)
            successMessages = new List<String>();
        successMessages.Add(successMessage);
        TempData["SuccessMessages"] = successMessages;
    }
    protected void AddWarningMessage(String warningMessage)
    {
        var warningMessages = TempData["WarningMessages"] as List<String>;
        if (warningMessages == null)
            warningMessages = new List<String>();
        warningMessages.Add(warningMessage);
        TempData["WarningMessages"] = warningMessages;
    }
    
  2. We have modified _Layout to display those messages:

        @if (@TempData.ContainsKey("ErrorMessages"))
        {
            foreach (var errorMessage in TempData["ErrorMessages"] as List<String>)
            {
            <div class="alert alert-error">
                <button type="button" class="close" data-dismiss="alert">&times;</button>
                @(new HtmlString(errorMessage))
            </div>
            }
        }
       //The same stuff for warnings and messages
    
  3. And in the controller we are using it as follows:

    public ActionResult CreateCustomer(Customer customer) {

    if (!ModelState.IsValid)
    {
    
        this.AddErrorMessage("Something wrong with your model");
    
        return View(customer);
    
    }
    

    }

We just return the same view, but with additional messages. So customer may check problem or see some warning/information message without loosing context.

1 Comment

Also a nice approach. I just try to avoid using Tempdata or Viewbag as much as I can
1

One of the reasons I don't like using RedirectToAction for error handling is that it changes the url of the page you're browsing, which is not the best user experience.

The simplest way would be to move your Error.cshtml into the /Views/Shared folder and make sure that file doesn't exist in the Controller throwing the error. Then you can just do this:

Error error = new Error { UserFriendlyText = "The friendly message", ErrorDescription = "Error to be logged" };
return RedirectToAction("Error", new System.Web.Routing.RouteValueDictionary(error));

By default MVC convention, it will search for the Error.cshtml file in the "Controller" directory, and upon not finding it then search in the Shared directory.

If you ever need to customize the error page for that controller, you would just create a new Error.cshtml page inside the Controller needing the custom error.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.