December 2017 : Online courses in ASP.NET MVC and Angular 4. Conducted by Bipin Joshi. Read more...
Registration for December 2017 batches of ASP.NET MVC / Core and Angular 4 online courses have already started. Conducted by Bipin Joshi. Book your seat today ! Click here for more details.

Untitled 1

Emitting Exceptions from LINQ to SQL classes in ASP.NET MVC Validation Summary

Recently while developing a sample application in ASP.NET MVC, I came across a peculiar situation. There was a LINQ to SQL class with several extensibility methods defined (OnXXXXChanging kind of methods where XXXX is the name of a property). The methods were performing some validation checks and in case of any violation were throwing exceptions. The LINQ to SQL class was working fine. However, at some places I wanted to display the validation errors (thrown by the class as exceptions) on the ASP.NET MVC views. That is where the problem began. No matter what exception you throw in the class the Validation Summary never displays the error message. This behavior is by design and is intended to hide sensitive exception details from the end user. In this specific, however, I wanted to reveal the exception message to the end user because all exceptions were basically validation errors and I was sure that they are not disclosing any sensitive system information. To overcome the problem I developed a custom action filter. The remainder of this article explains how the custom action filter works.

To understand the problem let's first reproduce the error by developing a sample LINQ to SQL class. Begin by creating a new ASP.NET MVC web application. Once created add a SQL database to it and create an Employee table in it. The schema of the Employee table is shown below:

Now add a new LINQ to SQL class to the web application and create the Employee LINQ to SQL class by dragging and dropping the Employees table from the Server Explorer onto the design surface of .dbml file.

Now, add a new class in the Models folder and modify it as shown below:

public partial class Employee
{
  partial void OnFirstNameChanging(string value)
  {
    if (value.Length < 3 || value.Length > 50)
    {
      throw new ValidationException("Invalid First Name.");
    }
  }
  partial void OnLastNameChanging(string value)
  {
    if (value.Length < 3 || value.Length > 50)
    {
      throw new ValidationException("Invalid Last Name.");
    }
  }
  ...
}

The code creates a partial class Employee and adds extensibility methods OnFirstNameChanging() and OnLastNameChanging() to it (you can add additional methods for other properties if you so wish). If there are any validation errors the methods simply throw ValidationException.

Add a controller named HomeController as shown below:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(Employee emp)
    {
        return View();
    }

}

The Index() action method simply renders the Index view. The Index view consists of three textboxes to enter FirstName, LastName and BirthDate and uses MVC Validations (see markup and screen capture below).

<% using (Html.BeginForm()) { %>
<%= Html.ValidationSummary()%>
<p>First Name :</p>
<%= Html.TextBoxFor(model => model.FirstName)%>
<%= Html.ValidationMessageFor(model => model.FirstName, "*")%>
<p>Last Name :</p>
<%= Html.TextBoxFor(model => model.LastName)%>
<%= Html.ValidationMessageFor(model => model.LastName, "*")%>
<p>Birth Date :</p>
<%= Html.TextBoxFor(model => model.BirthDate)%>
<%= Html.ValidationMessageFor(model => model.BirthDate, "*")%>
<p>
<input id="Submit1" type="submit" value="Submit" />
</p>
<%}%>

If you try entering invalid values in FirstName and LastName fields you will get errors as shown below:

Notice that though FirstName and LastName fields are showing error (*) there is no descriptive error message at all. The actual error messages ("Invalid First Name" and "Invalid Last Name") are suppressed by MVC framework for security reasons. However, since you are deliberately throwing these exceptions you know that showing them to the end user won't create any problem.

The solution is to create a custom action filter as shown below:

public class ShowExceptionDetailsInValidationSummary : FilterAttribute, IActionFilter
{
    public Type ExceptionType { get; set; }
    public string Keys { get; set; }
    public string ErrorMessage { get; set; }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Controller c = (Controller)filterContext.Controller;
        string[] keys = null;
        Dictionary<string, string> messages = new Dictionary<string, string>();

        if (Keys != null && Keys!=string.Empty)
        {
            keys=Keys.Split(',');
        }

        if (keys == null)
        {
            foreach (string key in c.ModelState.Keys)
            {
                foreach (ModelError err in c.ModelState[key].Errors)
                {
                    if (ExceptionType != null)
                    {
                        if (err.Exception.GetType().Equals(ExceptionType))
                        {
                            if (ErrorMessage == null || ErrorMessage == string.Empty)
                            {
                                messages.Add(key, err.Exception.Message);
                            }
                            else
                            {
                                messages.Add(key, ErrorMessage);
                            }
                        }
                    }
                    else
                    {
                        if (ErrorMessage == null || ErrorMessage == string.Empty)
                        {
                            messages.Add(key, err.Exception.Message);
                        }
                        else
                        {
                            messages.Add(key, ErrorMessage);
                        }
                    }
                }
            }
        }
        else
        {
            foreach (string key in keys)
            {
                if(c.ModelState.Keys.Contains(key))
                {
                    foreach (ModelError err in c.ModelState[key].Errors)
                    {
                        if (ExceptionType != null)
                        {
                            if (err.Exception.GetType().Equals(ExceptionType))
                            {
                                if (ErrorMessage == null || ErrorMessage == string.Empty)
                                {
                                    messages.Add(key, err.Exception.Message);
                                }
                                else
                                {
                                    messages.Add(key, ErrorMessage);
                                }
                            }
                        }
                        else
                        {
                            if (ErrorMessage == null || ErrorMessage == string.Empty)
                            {
                                messages.Add(key, err.Exception.Message);
                            }
                            else
                            {
                                messages.Add(key, ErrorMessage);
                            }
                        }
                    }
                }
            }
        }

        foreach (string key in messages.Keys)
        {
            c.ModelState.AddModelError(key, messages[key]);
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }

}

The ShowExceptionDetailsInValidationSummary action filter essentially loops through the Model Errors and then programmatically adds a model error message.

Once the ShowExceptionDetailsInValidationSummary action filter is ready you can decorate the Index() action method with it as shown below :

[HttpPost]
[ShowExceptionDetailsInValidationSummary]
public ActionResult Index(Employee emp)
{
    return View();
}

If you run the application again and try to enter invalid values for FirstName and LastName you will correctly get error messages as shown in the following figure.

By default ShowExceptionDetailsInValidationSummary action filter will display all the exception messages in the validation summary. You can also specify that only certain exceptions be displayed.

[ShowExceptionDetailsInValidationSummary(ExceptionType=typeof(ValidationException))]

Further you can also customize the error message and keys using ErrorMessage and Keys properties of ShowExceptionDetailsInValidationSummary class respectively.


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Meditation and Mindfulness to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 19 August 2011


Tags : ASP.NET MVC Security