Published:
27 Jan 2009
|
Abstract
In this article, Scott examines the various features of the recently released ASP.NET MVC 1.0 Release Candidate with the help of relevant explanations, source code, and screenshots. He begins with a detailed discussion of the new features oriented for Visual Studio. He then delves deep into the steps to be followed for adding and customizing Scaffold Templates. He then provides a short explanation of the MSBuild Task. He finally discusses the support for View Refactoring and also the various improvements made to Form Post, the ModelBinder API, Unit Testing, File Handling, and File Uploading in the new build of MVC 1.0. He concludes this long article by giving a short note about the various improvements available for AJAX. |
|
by Scott Guthrie
Feedback
|
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days):
67728/
119
|
|
|
Introduction |
Republished with Permission - Original Article
Today we shipped the ASP.NET MVC 1.0 Release Candidate
(RC). Click here to download it (note: the link just went live so
if it isn’t working wait a few minutes for the server you are hitting to
refresh). It works with both Visual Studio 2008 and Visual Web Developer 2008
(which is free).
Today’s RC is the last public release of ASP.NET MVC that
we’ll ship prior to the final “1.0” release. We expect to ship the final
ASP.NET MVC 1.0 release next month.
In addition to bug fixes, today’s build includes several new
features. It also includes some refinements to existing features based on
customer feedback. Please read the release notes that ship with the ASP.NET MVC download for
full details on all changes. The release notes include detailed instructions on how to upgrade
existing applications built with the ASP.NET MVC Beta to the RC.
|
Visual Studio Tooling Improvements |
The RC includes several new Visual Studio tooling features
(above and beyond the existing support in the beta – which I won’t cover
here). These features include:
|
Add Controller Command |
You can now type Ctrl-M, Ctrl-C within an ASP.NET MVC
project, or right-click on the /Controller folder and choose the
"Add->Controller" context menu item to create new controller
classes:
Figure 1
This will cause an “Add Controller” dialog to appear that
allows you to name the Controller to create, as well as optionally indicate
whether you wish to automatically “scaffold” common CRUD methods:
Figure 2
Clicking the “Add” button will cause the controller class to
be created and added to the project:
Figure 3
|
Add View Command |
You can now type Ctrl-M, Ctrl-V within a
Controller action method, or right-click within an action method and choose the
“Add View” context menu item to create new view templates:
Figure 4
-
This will cause an “Add View” dialog to appear
that allows you to name and create a new view (it is pre-populated with
convention-based options). It allows you to create “empty” view
templates, or automatically generate/scaffold view templates that are based on
the type of object passed to the view by the Controller action method.
The scaffolding infrastructure uses reflection when creating view templates –
so it can scaffold new templates based on any POCO (plain old CLR object)
passed to it. It does not have a dependency on any particular ORM or data
implementation.
For example, below we are indicating that we
want to scaffold a “List” view template based on the sequence of Product
objects we are passing from our action method above:
Figure 5
Clicking the “Add” button will cause a view template to be
created for us within the \Views\Products\ directory with a default “scaffold”
implementation:
Figure 6
We can then run our application and request
the /products URL within our browser to see a listing of our retrieved
products:
Figure 7
The RC ships with a number of built-in
scaffold templates: “Empty”, “List”, “Details”, “Edit” and “Create” (you can
also add your own scaffold templates – more details on this in a moment).
For example, to enable product editing support
we can implement the HTTP-GET version of our “Edit” action method on our
Products controller like below and then invoke the “Add View”
Figure 8
Within the “Add View” dialog we can indicate we are passing
a “Product” object to our view and choose the “Edit” template option to
scaffold it:
Figure 9
Clicking the “Add” button will cause an edit view template
to be created with a default scaffold implementation within the
\Views\Products\ directory
Figure 10
We can then run our application and request
the /products/edit/1 URL within our browser to edit the Product details:
Figure 11
To save edit changes we can implement the HTTP-POST version
of our “Edit” action method on our Products controller:
Figure 12
Notice in the code above how in the case of an
error (for example: someone enters a bogus string for a number value) we
redisplay the view. The “edit” and “create” scaffold templates contain
the HTML validation helper methods necessary to preserve user input and flag
invalid input elements in red when this happens:
Figure 13
You’ll rarely end up using a scaffold-created template
exactly as-is, and often will end up completely replacing it. But being
able to get an initial implementation up and running quickly, and having an
initial view template for your scenario that you can then easily tweak is
really useful.
Because the scaffold infrastructure supports scaffolding
views against any plain-old CLR object, you can use it with both domain model
objects (including those mapped with LINQ to SQL, LINQ to Entities, nHibernate,
LLBLGen Pro, SubSonic, and other popular ORM implementations) as well as to
create scaffolds with custom Presentation Model/ViewModel classes.
|
Adding and Customizing Scaffold Templates |
ASP.NET MVC’s scaffolding infrastructure is implemented
using Visual Studio’s built-in T4 templating architecture (Scott Hanselman has
a nice blog post on T4 here).
You can customize/override any of the built-in ASP.NET MVC
scaffold template implementations. You can also create additional
scaffold templates (for example: the “ScottGu Crazy Look” scaffold option) and
have them be displayed as options within the “Add View” dialog.
To customize/add scaffold templates at the machine-wide
level, open the “C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC\CodeTemplates”
folder:
Figure 14
The “AddController” sub-folder contains the scaffold
template for the “Add Controller” dialog. The “AddView” sub-folder
contains the scaffold templates for the “Add View” dialog:
Figure 15
The scaffold templates populated within the
“Add View” dialog are simply text files that have the “.tt” file-name
extension. These “.tt” text files contain inline C# or VB code that
executes when the template is selected.
You can open and edit any of the existing
files to customize the default scaffolding behavior. You can also add new
“.tt” template files – like I have above with the “Scott Crazy Look.tt”
file. When you add a new template file the “Add View” dialog will be
updated to automatically include it in the list of available scaffold options:
Figure 16
In addition to customizing/adding template
files at the machine level, you can also add/override them at the individual
project level. This also enables you to check-in the templates under
source control and easily use them across a team.
You can customize the scaffold templates at a
project level by adding a “CodeTemplates” folder underneath your project.
You can then have “AddController” and “AddView” sub-folders within it:
Figure 17
You can override any of the default
machine-wide templates simply be adding a “.tt” file with the same name to the
project. For example, above we are overriding the default “Controller.tt”
scaffold template used in “Add Controller” scenarios.
You can add new view-template scaffold files
to the list by placing them within the “AddView” folder. For example,
above we added a “Yet Another Crazy Look.tt” view template to our
project. When we use the “Add View” dialog we’ll now see a union of the
templates defined at the machine and project level:
Figure 18
Note: When you add “.tt” templates under the
\CodeTemplates folder make sure to set the “Custom Tool” property of each of
the “.tt” template files to an empty string value within the property grid
(otherwise you’ll get an error trying to run it). You might also need to
close and reopen the project to clear a spurious error from the error
list. We’ll be publishing more blog posts that cover creating/customizing
scaffolding templates shortly.
|
Go To Controller / Go To View |
The RC build now supports the ability to
quickly navigate between the Controllers and Views within your projects.
When your cursor is within a Controller action
method you can type Ctrl-M, Ctrl-G to quickly navigate to its corresponding
view template. You can also perform this same navigation jump by
right-clicking within the action method and selecting the “Go To View” menu
option:
Figure 19
In the example above we used the “Go To View” command within
the “Edit” action method of the ProductsController class. This will cause
the \Views\Products\Edit.aspx view template to be opened and have the default
focus within VS:
Figure 20
Within view templates you can also now type Ctrl-M, Ctrl-G
to quickly navigate to the view’s corresponding Controller class. You can
also perform this navigation jump by right-clicking within the view template
and selecting the “Go To Controller” menu option:
Figure 21
|
MSBuild Task for Compiling Views |
By default when you do a build on an ASP.NET MVC project it
compiles all code within the project, except for the code within view template
files. With the ASP.NET MVC Beta you had to roll your own MSBuild task if
you wanted to compile the code within view templates. The ASP.NET MVC RC
build now includes a built-in MSBuild task that you can use to include views as
part of the project compilation process. This will verify the syntax and
code included inline within all views, master pages, and partial views for the
application, and give you build errors if it encounters any problems.
For performance reasons we don't recommend running this for
quick compiles during development, but it is convenient to add to particular
build configuration profiles (for example: staging and deployment) and/or for
use with Build or CI (continuous integration) servers. Please review the
release notes for the steps to enable this.
|
View Refactoring Support |
The names of the files and folders under the \Views
application sub-folder will now automatically be updated when you perform
controller class rename or action method rename using the “Rename” refactoring
command in VS 2008. VS 2008 will apply the standard convention-based
naming pattern to existing view files/folders when the Controller class is
updated.
View Improvements
The RC build includes a number of view-specific enhancements
that were incorporated based on feedback during the preview releases.
Views without Code-Behind Files
Based on feedback we’ve changed view-templates to not have a
code-behind file by default. This change helps reinforce the purpose of
views in a MVC application (which are intended to be purely about rendering and
to not contain any non-rendering related code), and for most people eliminates
unused files in the project.
The RC build now adds C# and VB syntax support for
inheriting view templates from base classes that use generics. For
example, below we are using this with the Edit.aspx view template – whose
“inherits” attribute derives from the ViewPage<Product> type:
Figure 22
One nice benefit of not using a code-behind
file is that you'll now get immediate intellisense within view template files
when you add them to the project. With previous builds you had to do a
build/compile immediately after creating a view in order to get code
intellisense within it. The RC makes the workflow of adding and
immediately editing a view compile-free and much more seamless.
Important: If you are upgrading a ASP.NET MVC
project that was created with an earlier build make sure to follow the steps in
the release notes – the web.config file under the \Views directory needs to be
updated with some settings in order for the above generics based syntax to
work.
|
Model Property |
With previous builds of ASP.NET MVC, you
accessed the strongly typed model object passed to the view using the
ViewData.Model property:
Figure 23
The above syntax still works, although now
there is also a top-level "Model" property on ViewPage that you can
also use:
Figure 24
This property does the same thing as the
previous code sample - its main benefit is that it allows you to write the code
a little more concisely. It also allows you to avoid using the ViewData
dictionary in cases where you want the view template to only interact with the
strongly-typed model passed to it.
|
Setting the Title |
The default master-page template added to new
ASP.NET MVC projects now has an <asp:contentplaceholder/> element within
its <head> section. This makes it much easier for view templates to
control the <title> element of the HTML page rendered back – and not
require the Controller to explicitly pass a “title” parameter to configure it
(which was the default with previous ASP.NET MVC builds and we thought
questionable from a responsibilities perspective).
For example, to customize the <title> of
our Edit view to include the current product name we can now add the below code
to our Edit.aspx template to drive the title directly off of the model object
being passed the view:
Figure 25
The above code will then cause the browser to render the
title using the Product name at runtime:
Figure 26
In addition to setting the <title>
element, you can also use the above approach to dynamically add other
<head> elements at runtime. Another common scenario this is useful
with is configuring model/view specific <meta/> elements for search
engine optimization.
|
Strongly Typed HTML/AJAX Helpers |
One of the requests a few people have asked
for is the ability to use strongly-typed expression syntax (instead of strings)
when referring to the Model when using a View's HTML and AJAX helper objects.
With the beta build of ASP.NET MVC this wasn't
possible, since the HtmlHelper and AjaxHelper helper classes didn't expose the
model type in their signature, and so people had to build helper methods
directly off of the ViewPage<TModel> base class in order to achieve
this.
The ASP.NET MVC RC build introduces new
HtmlHelper<TModel> and AjaxHelper<TModel> types that are exposed on
the ViewPage<TModel> base class. These types now allow anyone to
build strongly-typed HTML and AJAX helper extensions that use expression syntax
to refer to the View's model. For example:
Figure 27
The HTML form helper extension methods in the core ASP.NET
MVC V1 assembly still use the non-expression based string syntax. The
“MVC Futures” assembly released today (which works with the RC) has a few
initial implementations of expression-syntax based form helper
methods. We are going to iterate on these a bit longer and then
consider adding them into the ASP.NET MVC core assembly in the next
release.
You can of course also add your own helper methods (using
either strings or strongly-typed expressions). The built-in HTML/AJAX
helper methods can also optionally be removed (because they are extension
methods) if you want to replace or override them with your own
|
Form Post Improvements |
The RC build includes a number of form-post specific
enhancements:
[Bind(Prefix=””)] No Longer Required for Common Scenarios
The RC build no longer requires you to explicitly use a
[Bind] attribute (or set its prefix value to “”) in order to map incoming form
post parameters that do not have a prefix to complex action method parameters.
To see what this means, let’s implement the “Create”
scenario for our ProductsController. We’ll begin by implementing the
HTTP-GET version of our “Create” action method. We’ll do this with code
below that returns a View based on an empty Product object:
Figure 28
We can then right-click within our action method, choose the
“Add View” command and scaffold a “create” view template that is based on a
Product:
Figure 29
Notice above how our Html.TextBox() helper
methods are referencing the “ProductName” and “SupplierID” properties on our
Product object. This will generate HTML markup like below where the input
“name” attributes are “ProductName” and “SupplierID”:
Figure 30
We can then implement the HTTP-POST version of
our “Create” action method. We’ll have our action method take a Product object
as a method parameter:
Figure 31
With the ASP.NET MVC Beta we would have had to
add a [Bind(Prefix=””)] attribute in front of our Product argument above – otherwise
the ASP.NET MVC binding infrastructure would have only looked for form post
values with a “productToCreate.” prefix (for example:
productToCreate.ProductName and productToCreate.SupplierID) and not found the
submitted values from our form (which don’t have a prefix).
With the RC build, the default action method
binders still first attempt to map a productToCreate.ProductName form value to
the Product object. If they don’t find such a value, though, they now
also attempt to map “ProductName” to the Product object. This makes
scenarios where you pass in complex objects to an action method syntactically
cleaner and less verbose. You can take advantage of this feature both
when mapping domain objects (like our Product object above) as well as with Presentation
Model/ViewModel classes (like a ProductViewModel class).
A completed implementation of our Create
action method (including basic input type error handling) might look like
below:
Figure 32
Now our create action will save the Product object if all
values are entered correctly. When a user attempts to create a Product
with invalid Product property values (for example: a string “Bogus” instead of
a valid Decimal value), the form will redisplay and flag the invalid input
elements in red:
Figure 33
|
ModelBinder API Improvements |
The model binding infrastructure within the ASP.NET MVC
Release Candidate has been refactored to add additional extensibility points to
enable custom binding and validation schemes. You can read more about
these details in the ASP.NET MVC RC release notes.
Model Binders can also now be registered for interfaces in
addition to classes.
IDataErrorInfo Support
The default model binder with ASP.NET MVC now supports
classes that implement the IDataErrorInfo interface. This enables a
common approach to raise validation error messages in a way that can be shared
across Windows Forms, WPF and now ASP.NET MVC applications.
|
Unit Testing Improvements |
The ASP.NET MVC RC includes some significant improvements to
unit testing:
ControllerContext changed to no longer derive from
RequestContext
The RC build includes a refactoring of the ControllerContext
class that significantly simplifies common unit testing scenarios. The
ControllerContext class no longer derives from RequestContext and now instead
encapsulates RequestContext and exposes it as a property. The properties
of ControllerContext and its derived types are also now virtual instead of
sealed – making it significantly easier to create mock objects.
To see how this helps, let’s consider an action method like
below that uses both the “Request” and “User” intrinsic objects:
Figure 34
Testing the above action method with previous ASP.NET MVC
builds would have required mocking RequestContext and ControllerContext (with
some non-obvious constructors that also brought in a RouteData object).
With the RC build we can now unit test it like below (using Moq to mock a ControllerContext for
our Controller that allows us to simulate the Request.IsAuthenticated and
User.Identity.Name properties):
Figure 35
The refactoring improvements made help out not just with
testing Controller actions – but also help with testing filters, routes, custom
actionresult types, and a variety of other scenarios.
|
AccountsController Unit Tests |
The ASP.NET MVC Project Template included with the RC build
now adds 25 pre-built unit tests that verify the behavior of the AccountsController
class (which is a controller added to the project by default to handle login
and account management scenarios). This makes refactoring/updating
AccountsController easier. The AccountsController implementation has also
been modified to more easily enable non-Membership Provider based credential
systems to be integrated.
Cross Site Request Forgery (CSRF) Protection
Cross-site request forgery (CSRF) attacks (also referred to
as XSRF attacks) cause users of a trusted browser agent to take unintended
actions on a site. These attacks rely on the fact that a user might still
be logged in to another site. A malicious Web site exploits this by
creating a request to the original site (for example: by linking to a URL on
the site using a <img src=””/> element on the hacker site). The request
is made using the user’s browser and thus with the user’s authentication token
and credentials. The attacker hopes that the user’s authentication or session
cookie is still valid and if so, the attacker can sometimes take disruptive
action. You can learn more about this hacking technique here.
The ASP.NET MVC RC now includes some built-in CSRF
protection helpers that can help mitigate CSRF attacks. For example, you
can now use the Html.AntiForgeryToken() helper to render a hidden input token
within forms:
Figure 36
This helper issues a HTTP cookie and renders a
hidden input element into our form. Malicious web-sites will not be able
to access both values.
We can then apply a new
[ValidateAntiForgeryToken] attribute onto any action method we want to protect:
Figure 37
This will check for the existence of the
appropriate tokens, and prevent our HTTP-POST action method from running if
they don’t match (reducing the chance of a successful CSRF attack).
|
File Handling Improvements |
The ASP.NET MVC RC includes a number of file handling
enhancements:
FileResult and File() helper method
The RC build adds a new FileResult class that is used to
indicate that a file is being returned as an ActionResult from a Controller
action method. The Controller base class also now has a set of File()
helper methods that make it easy to create and return a FileResult.
For example, let’s assume we are trying to build a photo
management site. We could define a simple “Photo” class like below that
encapsulates the details about a stored Photo:
Figure 38
We could then use the new File() helper method like below to
implement a “DisplayPhoto” action method on a PhotoManager controller that
could be used to render the Photo out of a database store. In the code
below we are passing the File() helper the bytes to render, as well as the
mime-type of the file. If we pointed a <img src=””/> element at our
action method URL the browser would display the photo inline within a page:
Figure 39
If we wanted an end-user to be able to download the photo
and save it locally, we could implement a “DownloadPhoto” action method like
below. In the code below we are passing a third parameter – which will
cause ASP.NET MVC to set a header that causes the browser to display a “Save
As…” dialog which is pre-populated with the filename we’ve supplied:
Figure 40
When a user clicks a link to the
/PhotoManager/DowloadPhoto/1232 URL they’ll be prompted to save the picture:
Figure 41
|
File Uploading Support |
The RC build also includes built-in model-binder support for
uploaded files and multi-part mime content.
For example, we could have a <form> whose enctype
attribute is set to “multipart/form-data” perform a post to the /PhotoManager/UploadPhoto
URL. If a <input type=”file” name=”fileToUpload”/> element was
within the form it would cause the file selected by the end-user to be passed
to our action method as an HttpPostedFileBase object:
Figure 42
We could then use the HttpPostedFileBase object to get
access to the raw bytes of the uploaded file, its mime-type, and optionally
save it to a database or disk.
|
AJAX Improvements |
The ASP.NET MVC RC includes a number of AJAX enhancements:
jQuery Intellisense Files included within ASP.NET MVC
Project Template
Newly created ASP.NET MVC projects now include both the
standard jQuery library (both full and compressed versions), as well as the
–vsdoc intellisense documentation file used by Visual Studio to provide richer
intellisense support for it (you can learn more about this here):
Figure 43
This enables rich jQuery JavaScript intellisense within
client-script blocks and JavaScript files:
Figure 44
Today’s RC build ships jQuery 1.2.6. We are planning
to ship the upcoming jQuery 1.3.1 release for the final ASP.NET MVC 1.0
release, and will include an updated JavaScript intellisense file for it.
Request.IsAjaxRequest Property
The Request.IsAjaxRequest property can be used to detect
whether a request is being sent from an AJAX call on the client (and is useful
for scenarios where you want to gracefully degrade if AJAX is not
enabled). The logic within this method was updated with the RC to now
recognize the “X-Requested-With” HTTP header (in addition to the form field
sent by ASP.NET AJAX). This is a well known header sent by JavaScript
libraries such a Prototype, jQuery, and Dojo – and now enables a unified way to
check for AJAX within an ASP.NET MVC request.
JavaScriptResult ActionResult and JavaScript() helper
method
The Controller base class now has a JavaScript() helper
method that returns a new ActionResult class of type JavaScriptResult.
This supports the ability to return raw JavaScript that will then be executed
on the client by the built-in ASP.NET MVC helper methods. This can be
useful for scenarios where you want to cause conditional JavaScript to execute
on the client based on server logic.
|
Summary |
We are pretty excited to be in the final “home stretch” of
ASP.NET MVC V1. Please report any issues you find with the RC build as
soon as possible so that we can get them resolved for the final release.
The team plans to carefully monitor feedback over the next few weeks, and
assuming no big issues come up ship the official V1 build next month.
Hope this helps,
Scott
|
Resources |
|
|
|
User Comments
Title:
Awesome article, great work!
Name:
George Loew
Date:
2009-03-25 12:20:05 AM
Comment:
excellent article, scott! It is very useful and contain excellent information about ASP.NET MVC framework. I have had a chance to try this ASP.NET MVC technology with my current host (asphostcentral.com) and everything works beautifully.
I am rating 5 stars for this article and I am looking forward to your next articles.
|
Title:
Cache Photo
Name:
Meysam
Date:
2009-03-05 2:14:50 AM
Comment:
Hi, Is it possible to send cache status (304) in response to subsequent requests for a photo?
|
|
Product Spotlight
|
|