The above code works with the previous "Preview 4"
release, and continues to work fine with "Preview 5". The
"Preview 5" release, though, adds several additional features that
will allow us to make this scenario even better.
These new features include:
The ability to publish a single action URL and dispatch it
differently depending on the HTTP Verb
Model Binders that allow rich parameter objects to be
constructed from form input values and passed to action methods
Helper methods that enable incoming form input values to be
mapped to existing model object instances within action methods
Improved support for handling input and validation errors
(for example: automatically highlighting bad fields and preserving end-user
entered form values when the form is redisplayed to the user)
I'll use the remainder of this blog post to drill into each
of these scenarios.
[AcceptVerbs] and [ActionName] attributes
In our sample above we implemented our product add scenario
across two action methods: "Create" and "Save". One
motivation for partitioning the implementation like this is that it makes our
Controller code cleaner and easier to read.
The downside to using two actions in this scenario, though,
is that we end up publishing two URLs from our site: "/Products/Create"
and "/Products/Save". This gets problematic in scenarios where
we need to redisplay the HTML form because of an input error - since the URL of
the redisplayed form in the error scenario will end up being "/Products/Save"
instead of "/Products/Create" (because "Save" that was the
URL the form was posted to). If an end-user adds this redisplayed page to
their browser favorites, or copy/pastes the URL and emails it to a friend, they
will end up saving the wrong URL - and will likely have an error when they try
and access it later. Publishing two URLs can also cause problems with
some search engines if your site is crawled and they attempt to automatically
traverse your action attributes.
One way to work around these issues is to publish a single
"/Products/Create" URL, and then have different server logic
depending on whether it is a GET or POST request. One common approach
used to-do this with other web MVC frameworks is to simply have a giant if/else
statement within the action method and branch accordingly:
The downside with the above approach, though,
is that it can make the action implementation harder to read, as well as harder
to test.
ASP.NET MVC "Preview 5" now offers a
better option to handle this scenario. You can create overloaded
implementations of action methods, and use a new [AcceptVerbs] attribute to
have ASP.NET MVC filter how they are dispatched. For example, below we
can declare two Create action methods - one that will be called in GET
scenarios, and one that will be called in POST scenarios:
This approach avoids the need for giant
"if/else" statement within your action methods, and enables a cleaner
structuring of your action logic. It also eliminates the need to mock the
Request object in order to test these two different scenarios.
You can also optionally now use a new
[ActionName] attribute to allow the method name implementation on your
controller class to be different than that from the published URL. For
example, if rather than having two overloaded Create methods in your controller
you instead wanted to have the POST method be named "Save", you could
apply the [ActionName] attribute to it like so:
Above we have the same controller method signature (Create
and Save) that we had in our initial form post sample. The difference,
though, is that we are now publishing a single URL (/Products/Create) and are
automatically varying the handling based on the incoming HTTP verb (and so are
browser favorites and search engine friendly).