Home / AJAX

How Do I Post Data to a Controller Action?

RSS
Modified on 2010/07/26 07:01 by James Chambers Categorized as Uncategorized

Problem

Given a form I've created for my web site users, how do I submit the data to a controller action using jQuery?

Solution

Depending on the type of data and even the type of client you are working with there can be a number of ways to deal with sending the data to a controller action. ASP.NET MVC continues to evolve in this area and make data submission friendly to the developer.

Using Form-URLEncoded values

When we do a request via POST the server sees the values we pass in as form-urlencoded content. This is perfect for most scenarios in ASP.NET MVC, as the framework provides built in mechanisms for model binding and validation.

A Simple Example

Let's look at the simplest of scenarios, passing in just one value.

Image

Try Me!

Here, we're anticipating a single string as input from the user. To send this information from the client to the action, we'll use a bit of jQuery to add an event handler to our button click. In the handler we'll capture the name that was entered and then send the data by calling $.post().
// jQuery's doc ready handler
$(function () {

$("#submit-name").button().click(function () {

var nameEntered = $("#your-name").val()

$.post( '<%= Url.Action("SimplePost") %>', { submittedName: nameEntered }, handleSuccess );

});

});

function handleSuccess(content) { $("#name-input").html(content); $("#name-input").addClass("easy-notification"); }

The data that is posted is packed into a data map where we specify that the parameter submittedName will have the value nameEntered, which was pulled from the form value.

Capturing this data in our controller is as simple as writing a method that accepts a single parameter. This parameter must be the same as the parameter name we used in our data map, submittedName.
[HttpPost]
public ContentResult SimplePost(string submittedName)
{
    string result = string.Format("See how easy that was, {0}!?", submittedName);
    return new ContentResult { Content = result };
}

Sending A List of Data

There are obviously more interesting sets of data that our users will work with. Let's consider the example of sorting some data using jQuery UI and extracting the result.

Image

Try Me!

Here the user is asked to drag and drop the list into the order they prefer. This list uses the jQuery UI library's sortable plugin to give us drag-and-drop support for a simple unordered list. The sortable plugin also provides a way to extract the element IDs from the list using the toArray method. Our script to prepare the data will look as follows:
// submit the data from our sorted list
function postSortOrder() {
    $.ajax({
        data: { orderedHeroIds: $("#hero-list").sortable('toArray') },
        type: 'POST',
        traditional: true,
        success: updateMessage
    });
}

We would call the above postSortOrder() method in the click handler of a button on our page. Here, we are still doing a POST, but we have to use the $.ajax() method for a little more control over the array serialization. Namely, we are setting the traditional property to true so that the array is encoded in a format that works with the MVC framework. Finally, we call updateMessage, which is a function that will use the returned value to update the client.

Folks, here is were the MVC Framework really shines. Take a look at the important parts of our controller action:
[HttpPost]
public ContentResult SortedList(List<int> orderedHeroIds)
{

foreach (var heroId in orderedHeroIds) { // do something interesting with the new order // (update db, etc) }

// build our result // ...

return result; }

As you can see, we can create our signature with a generic list of int as a parameter. The serialized list of ids that we posted to the controller is converted to List<int> by the MVC framework.

Now that you see how it's done, why not give it one more try? Try Me!

Submitting a Form

Let's have a look at another set up where we implement a form on the page. Here we want to submit the request and process the response with jQuery. In the following code create-hero is the ID of the form element on the page. We intercept the submit event and then stop the default post from firing by calling preventDefault().
$(function () {

$("#create-hero").submit(function (event) { // url-encode the form data var formData = $(this).serialize();

// post the data to the controller $.post( '<%= Url.Action("Index") %>', formData, processResult // our success callback );

event.preventDefault(); }); });
Image

Calling .serialize() transforms our form values to the form-urlencoded values required to send the data to the controller, resulting in the string FullName=Lazino&Power=procrastination.

Try Me!

To handle this in our action we work much the same way as we have so far: specify the type of object we want to accept and allow the MVC framework to take care of the mapping. We can then manipulate the data in whatever way we need to and save it to our database.
[HttpPost]
public ActionResult Index(SuperHero newHero)
{
    // add the hero to the database, example:

// heroRepository.AddHero(newHero) // heroRepository.Save()

return PartialView("HeroAdded", newHero); }

Posting using JSON

But what if we're working with a front end that is trying to send us JSON-formatted data? There are a number of issues that surface right away. Before we get to the solution, I want to walk through the pitfalls and see where the complications arise.

Image

Here is a form that has a number of fields on it. Let's assume there is some driver (perhaps alternate intefaces) that requires us to POST new recipes as JSON data.

Only recently have the leading browsers started implementing a native method for converting objects to JSON data. This has lead to a number of plugins and scripts that have surfaced with varying implementations. JSON.org has provided a library, json2.js, that allows for these native implementations to execute if the toJSON() method exists in the browser; otherwise it will transform our object for us internally using the JSON.stringify() method.

Now that we have a way to convert the data to JSON, we need to next deal with the fact that JSON encoded data is stored in a string. If we do a standard $.post(...) the server interprets the data as application/x-www-form-urlencoded. If the data is serialized as JSON and passed in as the data parameter for the post there will be just one form value: the string of JSON data.

To see the implications here, let's look at the JavaScript version of an object designed to store recipes for sweets which, by name, matches a corresponding class defined in our code base. The client-side object is defined as such:
function CookieRecipe() {
    this.Name = "";
    this.Description = "";
    this.PrepTimeMinutes = 0;
    this.BakingTimeMinutes = 0;
    this.Servings = 0;
}

When the user completes entry on the page, they click a button to send the recipe information to our controller action. The jQuery handler for the event is defined as follows:
$("#create-recipe").click(function () {

var myRecipe = new CookieRecipe();

myRecipe.Name = $("#Name").val(); myRecipe.Description = $("#Description").val(); myRecipe.PrepTimeMinutes = $("#PrepTimeMinutes").val(); myRecipe.BakingTimeMinutes = $("#BakingTimeMinutes").val(); myRecipe.Servings = $("#Servings").val();

var postData = JSON.stringify(myRecipe);

$.post('<%= Url.Action("SaveRecipeModel") %>', postData, recipeSuccess);

});

The data is entered in the form, stored in our object, converted to JSON and sent to our controller action. Now, we're getting somewhere...or are we?

We might take the next step of defining a controller action with a signature similar to the following:
[HttpPost]
public ActionResult SaveRecipe(CookieRecipe recipe)
{
    // ...
}

Unfortunately this approach will not work out-of the box, and this is where the single-string, form-urlencoded data has kicked us in the proverbial rear-end: we've lost the MVC framework's model binding! The recipe parameter will be null.

But we do have the string...let's not forget that we can modify the signature of that method and get the form values out of the request. We can then use the JavaScriptSerializer object to convert the string to our desired object type. The updated method might look like this:
[HttpPost]
public ActionResult SaveRecipe(FormCollection form)
{
    JavaScriptSerializer js = new JavaScriptSerializer();
    var recipe = js.Deserialize<CookieRecipe>(form[0]);           

// do some processing... // ...

return PartialView(recipe); }

Again, you might think we're flying, but we've lost another important aspect of what the MVC framework has to offer: model validataion. If the user has entered invalid data we don't know until the binding fails. Rather than getting a graceful error message on the client, we get an exception on the server and the controller action bails out.

Thankfully a number of folks have been working through this. Phil Haack posted a blog entry on how to sort through the above problems by using a custom value provider, something you could do on your own, but is now available through the ASP.NET MVC futures library.

To get started, you need to download the ASP.NET MVC futures library (available here) and reference the DLL in your project. Next, modify your Global.asax file's Application_Start() method to register the JsonValueProviderFactory:
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}

Finally, we need to revist our code that performs the POST to our controller. The JsonValueProvider will look for requests that are sent with a content type of application/json so we can't use the default $.post() method. We'll update our code to make the call through $.ajax() and specify our contentType explicitly.

$.ajax({
    url: '<%= Url.Action("SaveRecipe") %>',
    type: 'POST',
    data: postData,
    contentType: 'application/json; charset=utf-8',
    success: recipeSuccess
});

Now, back on our controller, we can update our action to accept the model type we originally had hoped for. The updated action is closer to our first version, except that now the application/json content type tells the MVC framework to use our new value provider. Our object is correctly populated and we can implement the method much like any other action:
[HttpPost]
public ActionResult SaveRecipe(CookieRecipe recipe)
{
    // save the recipe in the database, ex.
    // recipeRepository.AddRecipe(recipe);
    // recipeRepository.Save();

return PartialView(recipe); }

Try Me!

Let's quickly recap the above steps to send JSON data to the controller:
  • Download the json2 library for JSON serialization on the client
  • Download the MVC futures library to use the JSON value provider
  • Modify Global.asax's ApplicationStart to register the factory
  • Use $.ajax() to make the request with the proper content type

Wrapping Up

As you can see, posting different kinds of data with jQuery can be quite straightforward. With powerful helpers, plugins and freely available libraries, data from your pages can easily be interpretted by the MVC framework. JSON-style posting still requires a bit of setup, but support through the futures library provides a good, streamlined approach to model binding the data in our controller actions.

About the Author

James Chambers is a Senior Software Developer in Canada, where he has the local record for the 100m dash with snow shoes (currenly posting a 48.94s time). His development passions are fueled by new tech, new tools and mentoring others. Follow his coding adventures at Mister James.
  Name Size
- posting-forms.PNG 56.76 KB
- posting-json.PNG 41.47 KB
- posting-lists.PNG 52.13 KB
- posting-simple.PNG 51.36 KB