ASP.NET MVC - Post to controllers

When you first start out using the ASP.NET MVC framework, it might not be apparent the many different ways you can solve the same problem. One example of this is when you want to post form data from a view to a controller.

Most of the official examples when the ASP.NET MVC framework was in early preview showed how to use the HttpRequest object to gather the form data inside the controller.


public ActionResult Edit()
{
SalesOrderForm form = new SalesOrderForm();

string customerName = Request.Form["CustomerName"];
string address = Request.Form["CustomerAddress"];
string orderNumber = Request.Form["OrderNumber"];

return View(form);
}


In this way, you can use the Request object and pull the data out. I don't like to tie the controller directly to the HttpRequest object without any abstraction and I really dislike having use magic strings. It brings back not so fond memories of classical ASP for those that have used the Microsoft stack long enough. Thankfully, very few examples uses this way of gathering the form data.

An alternative way to pass the form data is to create an action method in the controller which will accept the values posted from the view. The view form fields must match the same names in the controller. A subset of our view shows the fields we are concerned with.


<% using (Html.BeginForm()) { %>

Fields



<%= Html.TextBox("CustomerName") %>




<%= Html.TextBox("Address") %>




<%= Html.TextBox("OrderNumber") %>






<% } %>


The controller action method must have matching variable names. The action method is pretty simple in our case and we just need the matching variable names.


[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string customerName, string address, string orderNumber)
{

string myCustomerName = customerName;
string myAddress = address;
string orderNumber = orderNumber;

// Persist the data into our repository

return View();
}


It's a pretty simple exercise to match them but quickly gets unruly if we have more than a handful of parameters we need to pass in. Thankfully, there are a few better ways to handle it. In the next scenario we will use the FormCollection object and use the MVC framework to map it to a view model.


public class SalesOrderForm
{
public string CustomerName { get; set; }
public string Address { get; set; }
public string OrderNumber { get; set; }
}



[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection formCol)
{
SalesOrderForm form = new SalesOrderForm();

UpdateModel(form);

// Persist to our repository

return View(form);
}


Our action method only needs to accept the FormCollection object. We instantiate our view model object and call the UpdateModel method which is method of the base class System.Web.Mvc.Controller. Any controller class that you create using the Visual Studio MVC template is subclassed from it and have this method available. Through reflection, it converts between the properties and the form data passed in. This gives us a pretty clean way of mapping the form data to our view model.

There is an even cleaner way to map the form data to a view model.


[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit([Bind(Prefix = "")]SalesOrderForm salesForm)
{
// Persist to the repository

return View(salesForm);
}


By using the Bind attribute, the controller will attempt to match up the properties with the form data. I think this is the cleanest way to map between the view and the view model and this is the way I usually handle form data that is posted to the controllers.

  1. gravatar

    # by Anonymous - November 1, 2010 at 3:04 PM

    This post has been very helpful. I was performing some testing following the last example and by mistake was able to do it without the Bind property. Is this property really necessary or is going to become a problem at runtime? Does the html form automatically bind to the model through the controller action?

  2. gravatar

    # by Magnus Lassi - November 1, 2010 at 9:04 PM

    @mistel, the Bind isn't necessary any longer for most scenarios, it was just pre-RC for MVC 1.0 at the time.

    The form is automatically bound via the controller and the ViewPage type.