jqueryasp.net-mvcajaxvalidationxval

ASP.NET MVC Validation Framework when posting via jquery $.ajax?


There is a plenty of very good post and explanations how to implement validation with ASP.NET MVC, and I prefer one of these:

However, I really like to call ActionMethods via jquery $.ajax method. One of the reasons why I want to use $.ajax is because there will be a lot of partial views loaded into the page dynamically (even the form for entity creation) via $.ajax calls and I can't just return the view - I'll lose all dynamically loaded content.

To give you a better view on the problem, I'll post some simple code to explain how would I like to call controllers actions and handle responses in client, jquery code.

The controllers ActionMethod:

    public ActionResult CreateCustomer(string name, string accountNumber)
    {
        try
        {
            CustomerService.InsertCustomer(name, accountNumber);

            return Json(new ActionInfo()
            {
                Success = true,
                Message = "Customer Is Successfully Created"
            });

        }
        catch (Exception ex)
        {
            return Json(new ActionInfo()
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

Calling and handling in client code:

$.ajax({
type: "POST",
url: $form.attr('action'),// /MyController/CreateCustomer
data: $form.serialize(),
error: HandleUnespectedError,
dataType: "json",
success: function(response) {

    if (response.Success)
        alert("Success: " + response.Message);
    else
        alert("Error: " + response.Message);
}});

Is there a good way to make some of these validation frameworks to work the way I need? I know that I can add validation errors in ActionInfo, and then handle it in client, but that would be already a building of my one validation, I believe.


Solution

  • I have had great success doing validation via AJAX using data annotations attributes. In order to check the validity of your data, you will want to use the controller's ModelState property, which has a property of its own called IsValid. I strongly suggest taking a peek at the data annotations validation attributes tutorial from the official ASP.NET MVC site.

    First off, you will want to modify your controller action to accept your model object as a parameter, rather than a separate name and account number. This will make performing the validation, which I will demonstrate below, much simpler. From your example, my best guess is that your model object is, or would be, called Customer. You might have the following code to define the model object and your controller action...

    // model object
    public class Customer
    {
      public Int32 Id {get; set;}
      public String Name {get; set;}
      public String AccountNumber {get; set;}
    }
    
    // controller
    public class CustomerController : Controller
    {
      public ActionResult CreateCustomer( [Bind(Exclude = "Id")] Customer customer )
      {
         // controller action code
      }
    }
    


    Make sure your form fields are named to match the names of the properties of the Customer object so ASP.NET MVC can automagically bind them. The "Bind" attribute, in this case, is telling ASP.NET MVC to ignore the "Id" property of the Customer class when binding form fields to model properties. Since this is a new customer, we don't have an Id yet, so we can safely leave the Id as whatever the default value is and leave the data layer to figure out how best to generate it.

    Once the controller has constructed the model object for the action method, its validity can be easily checked via the ModelState.IsValid property. As one might expect, it will return true if the model properties are valid, or false if 1 or more properties are invalid.

    From the original question, it appears that the CustomerService.InsertCustomer method is throwing exceptions when validation fails. This is completely unnecessary. InsertCustomer should only need to perform whatever data operations are necessary for inserting the new record. Unless you wish to abstract implementation specific exceptions, like SqlException, InsertCustomer should not really need to catch or throw any exceptions, but can most likely just let any exceptions bubble up to the controller (or whoever the caller may be).

    The end result of all of this, might be a controller action that looks like the following:

    public ActionResult CreateCustomer( [Bind(Exclude = "Id")] Customer customer )
    {
      // model is invalid
      if (!ModelState.IsValid)
      {
        return Json(new ActionInfo()
        {
          Success = false,
          Message = "Validation failed" // you will probably want a more robust message :-)
        });
      }
    
      // service method accepts a Customer object rather than arbitrary strings  
      CustomerService.InsertCustomer(customer);
    
      return Json(new ActionInfo()
      {
        Success = true,
        Message = "Customer created successfully."
      });
    
    }
    


    If you want to report unexpected errors, like database related exceptions, then you can certainly add a try/catch block around the call to InsertCustomer, and return the necessary result for displaying the error message back to the client.