MVC 4 Ajax.BeginForm and ModelState.AddModelError

2019-05-21 08:37发布

问题:

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 - 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.

回答1:

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:

  • MVC 4 Ajax.BeginForm and ModelState.AddModelError
  • ASP.NET MVC “Ajax.BeginForm” executes OnSuccess even though model is not valid