ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummary
Asked Answered
N

1

2

I've been trying to follow the validation tutorials and examples on the web, such as from David Hayden's Blog and the official ASP.Net MVC Tutorials, but I can't get the below code to display the actual validation errors. If I have a view that looks something like this:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Parent>" %>

<%-- ... content stuff ... --%>

<%= Html.ValidationSummary("Edit was unsuccessful. Correct errors and retry.") %>
<% using (Html.BeginForm()) {%>

<%-- ... "Parent" editor form stuff... --%>

        <p>
            <label for="Age">Age:</label>
            <%= Html.TextBox("Age", Model.Age)%>
            <%= Html.ValidationMessage("Age", "*")%>
        </p>

<%-- etc... --%>

For a model class that looks like this:

public class Parent
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
    public int Age { get; set; }
    public int Id { get; set; }
}

Whenever I enter an invalid Age (since Age is declared as an int), such as "xxx" (non-integer), the view does correctly display the message "Edit was unsuccessful. Correct errors and retry" at the top of the screen, as well as highlighting the Age text box and put a red asterisk next to it, indicating the error. However, no list of error messages is displayed with the ValidationSummary. When I do my own validation (e.g.: for LastName below), the message displays correctly, but the built-in validation of TryUpdateModel does not seem to display a message when a field has an illegal value.

Here is the action invoked in my controller code:

    [AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult EditParent(int id, FormCollection collection)
    {
        // Get an updated version of the Parent from the repository:
        Parent currentParent = theParentService.Read(id);

        // Exclude database "Id" from the update:
        TryUpdateModel(currentParent, null, null, new string[]{"Id"});
        if (String.IsNullOrEmpty(currentParent.LastName))
            ModelState.AddModelError("LastName", "Last name can't be empty.");
        if (!ModelState.IsValid)
            return View(currentParent);

        theParentService.Update(currentParent);
        return View(currentParent);
    }

What did I miss?

Naught answered 1/9, 2009 at 13:16 Comment(0)
N
2

I downloaded and looked at the ASP.NET MVC v1.0 source code from Microsoft, and discovered that, either by accident or by design, there isn't a way to do what I want to do, at least by default. Apparently during a call to UpdateModel or TryUpdateModel, if validation of an integer (for example) fails, an ErrorMessage is not explicitly set in the ModelError associated with the ModelState for the bad value, but instead the Exception property is set. According to the code from the MVC ValidationExtensions, the following code is used to fetch the error text:

string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);

Notice the null parameter for the modelState is passed. The GetUserErrorMEssageOrDefault method then begins like this:

private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState) {
    if (!String.IsNullOrEmpty(error.ErrorMessage)) {
        return error.ErrorMessage;
    }
    if (modelState == null) {
        return null;
    }

    // Remaining code to fetch displayed string value...
}

So, if the ModelError.ErrorMessage property is empty (which I verified that it is when trying to set a non-integer value to a declared int), MVC goes on to check the ModelState, which we already discovered is null, thus null is returned for any Exception ModelError. So, at this point, my 2 best work-around ideas to this issue are:

  1. Create a custom Validation extension that correctly returns an appropriate message when ErrorMessage is not set, but Exception is set.
  2. Create a pre-processing function that is called in the controller if ModelState.IsValid returns false. The pre-processing function would look for values in the ModelState where the ErrorMessage is not set, but the Exception is set, and then derive an appropriate message using the ModelState.Value.AttemptedValue.

Any other ideas?

Naught answered 1/9, 2009 at 18:48 Comment(2)
I came to this same realization myself, but through a little trial and error. Your #2 is roughly what I ended up having to do.Austenite
Do you know if there is a way of doing this in MVC3? I'm about to create a global action filter that copies from my exception to the ErrorMessage property but it seems odd that I should have to!Chickweed

© 2022 - 2024 — McMap. All rights reserved.