I'm trying to get errors to show up after an ajax submit has returned an error. I'm not sure what I'm missing, but I can't get it to work. This question is basically the same thing - https://stackoverflow.com/questions/10406998/modelstate-addmodelerror-is-not-being-displayed-inside-my-view but I'm still not having any luck. My experience with Ajax and MVC (any version) is still a bit limited. Here is a very simple example, most of which I took from the previous link.

View: test.cshtml

@model TestProject.VisitLabResult

@Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
@Scripts.Render("~/Scripts/ckeditor/ckeditor.js")

@{
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        Url = Url.Action("test"),
        HttpMethod = "Post",
        LoadingElementId = "loading",
        LoadingElementDuration = 500,
        OnSuccess = "processData"
    };
}
@Html.ValidationMessage("CustomError")


<div id="loading" class="load" style="display:none">
    <p>Saving...</p>
</div>

<table>
@for (int item = 0; item < 10; item++)
{
    <tr id = @item>
    @using (Ajax.BeginForm(ajaxOpts))
    {  
        @Html.ValidationSummary(true)

        @Html.AntiForgeryToken()
    
        <td>
            <input type="submit" value="Create" />
        </td>

        <td id = @(item.ToString() + "td")>
        </td>
    }
    </tr>
    }
</table>

Controller: HomeController.cs

public ActionResult test()
{
    return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult test(VisitLabResult vlr, int visitid = 28)
{
    try
    {
        if (ModelState.IsValid)
        {
            if (Request.IsAjaxRequest())
            {
                throw new Exception("error");
            }
            else
                return View(vlr);
        }
        else
            return View(vlr);
    }
    catch (Exception ex)
    {
        ModelState.AddModelError("CustomError", "The Same test Type might have been already created, go back to the Visit page to see the available Lab Tests");
        return View(vlr);
    }
}

Model

public class VisitLabResult
{
    public int visitid { get; set; }
}

If it is an Ajax request I throw an error and it's caught and an error is added to ModelState. That error never shows up on the page though. Am I approaching this the right way at all? Or do I need to take a different route? I appreciate any help.

Just to clarify the solution for other people hitting this question. The ajax helper fires OnSuccess vs OnFailure based on the returned HTTP Code per the AjaxOptions docs:

OnSuccess: This function is called if the response status is in the 200 range.
OnFailure: This function is called if the response status is not in the 200 range.

In other words, you have to manually specify that there was a failure when returning your ActionResult by changing the Response.StatusCode and then returning whatever values you're expecting in your OnFailure js method. You can drive that based on any business logic you want (i.e. catch Exception ex) or !ModelState.IsValid ...)

[HttpPost]
public ActionResult Search(Person model) 
{
  if (ModelState.IsValid) {
    // if valid, return a HTML view inserted by AJAX helper
    var results = PersonRepository.Get(model)
    return PartialView("Resulsts", vm);
    
  } else {
  	// if invalid, return a JSON object and handle with OnFailure method
    Response.StatusCode = (int)HttpStatusCode.BadRequest;
    return Json(new { errors = ModelState.Values.SelectMany(v => v.Errors) });

  }
}

Further Reading: