jQuery Validate, ASP.NET MVC ModelState Errors (Async POST)

You could return the ModelState errors. This is untested, but something like this should work.

return Json(new
            {
                success = false,
                errors = ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                                .Select(m => m.ErrorMessage).ToArray()
            });

Edit: To display the errors in a view you just check to see if you were successful, and if not, iterate over the list of errors and put them where you show your errors.

if(!result.success) {
    for(var error in result.errors) {
        $('#errorMessages').append(error + '<br />');
    }
}

I prefer Darin's answer for most projects, but your consumer may not always be your own application in which case returning a list of errors is more appropriate since it would allow the consumer to display the errors however they want.


Instead of sending JSON in case of error I would put the form inside a partial and then have the controller action return this partial in case of error. The problem with JSON is that you could in fact fetch the errors from the ModelState using LINQ, but it could be a PITA to show them on the view.

So:

<div id="myform">
    @Html.Partial("_MyForm")
</div>

and then inside _MyForm.cshtml:

@model CustomerViewModel
@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.Foo)
    @Html.ValidationMessageFor(x => x.Foo)
    <br />
    @Html.EditorFor(x => x.Bar)
    @Html.ValidationMessageFor(x => x.Bar)
    <br />
    <input type="submit" value="OK" />
}

and the controller action would become:

[HttpPost]
public ActionResult SaveCustomer(CustomerViewModel model)
{
    if (!ModelState.IsValid)
    {
        return PartialView("_MyForm", model);
    }
    return Json(new { success = true });
}

and the last step is to AJAXify this form which could be done in a separate javascript file:

$(function () {
    $('#myform').delegate('form', 'submit', function () {
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                if (result.success) { 
                    // We have a JSON object in case of success
                    alert('success');
                } else {
                    // We have the partial with errors in case of failure
                    // so all we have to do is update the DOM
                    $('#myform').html(result);
                }
            }
        });
        return false;
    });
});