Ajax.BeginForm always fires OnSuccess method, even when data is bad

BKahuna picture BKahuna · Dec 5, 2012 · Viewed 7.3k times · Source

I'm trying to submit a form to a controller action and process the response based on OnSuccess or OnFailure. The problem is that even when the data isn't valid and I fail the ModelState.IsValid test, the OnSuccess method is called. The OnFailure method should be called.

My View:

@using (Ajax.BeginForm("UpdateCategory", "Home", null, new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "categoryForm", OnSuccess = "alert('success');", OnFailure = "alert('failure');" }, new { id = "formEditCategory" }))
{
    @Html.ValidationSummary(true)

    @Html.HiddenFor(model => model.CategoryID)

    <div>
        <div class="editor-label">
            @Html.LabelFor(model => model.CategoryName)
        </div>
        <div class="small-multiline-field">
            @Html.EditorFor(model => model.CategoryName)
        </div>
        <div class="validationMsg">
            @Html.ValidationMessageFor(model => model.CategoryName)
        </div>
    </div>
}

My Controller Action:

[HttpPost]
public ActionResult UpdateCategory(CategoryVM category)
{
    try
    {
        if (ModelState.IsValid)
        {
            var itemService = new ItemViewModels();
            itemService.UpdateCategory(category);
        }
    }
    catch (DataException)
    {
        //Log the error (add a variable name after DataException)
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
    }

    return PartialView("EditCategoryInfo", category);
}

My ViewModel:

public class CategoryVM
{
    public int CategoryID { get; set; }

    [StringLength(75, ErrorMessage = "Category Name must be under 75 characters.")]
    [DataType(DataType.MultilineText)]
    [Display(Name = "Name")]
    public string CategoryName { get; set; }

    [StringLength(3800, ErrorMessage = "Category Description must be under 3800 characters.")]
    [DataType(DataType.MultilineText)]
    [Display(Name = "Description")]
    [AllowHtml]
    public string CategoryDesc { get; set; }

    [Display(Name = "Display on Web")]
    public bool DisplayOnWeb { get; set; }
}

So if I enter a string in the CategoryName field that exceeds 75 characters I can see that the form does not pass the ModelState.IsValid test and the view is sent back annoted with the "Category Name must be under 75 characters." error message. But rather than firing the OnFailure event, it fires the OnSuccess event. Why?

Thanks in advance.

Answer

Jack picture Jack · Dec 6, 2012

Since you've caught the exception, what you are returning is a PartialView with HTTP status code of 200. That's why OnSuccess gets triggered.

What you can do instead is explicitly set the response code to 400(bad request)

[HttpPost]
public ActionResult UpdateCategory(CategoryVM category)
{
    try
    {
        if (ModelState.IsValid)
        {
            var itemService = new ItemViewModels();
            itemService.UpdateCategory(category);
        }
    }
    catch (DataException)
    {
        //Log the error (add a variable name after DataException)
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
        Response.StatusCode = 400;
    }

    return PartialView("EditCategoryInfo", category);
}