The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.
Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by st3phenchow, 2019-06-07 04:47:59

ASP.NET MVC 5 Professional

ASP.NET MVC 5 Professional

Mobile Support ❘ 465

What might not be immediately obvious is that the page layout actually changes subtly at this
smaller size to optimize for the new dimensions. For example, the header navigation is collapsed
from ive separate text links to a single drop-down menu, as shown in Figure 16-3.

FIGURE 16-3

Scrolling down further, you can see the other simpliications to the mobile view to tighten it up and
maximize the screen real estate. Although the changes are subtle, they make a difference.

466 ❘ CHAPTER 16 ADVANCED TOPICS

For example, form ields shown in the Register view (also visible in Figure 16-3) are appropriately
sized for touch entry on a mobile device.

These templates use adaptive rendering to automatically scale the page depending on page width.
Note that I didn’t say that the application scales the page by guessing whether the user is on a
mobile device based on headers, or other clues. Instead, this page makes use of two commonly sup-
ported browser features: the Viewport meta tag and CSS media queries.

The Viewport Meta Tag

The majority of web pages have been created without any thought to how they’ll appear in smaller
form factors, and mobile browsers have long struggled with guessing how best to display them.
Designs that are primarily focused on semantically structured textual content can be reformatted to
make the text readable, but sites with rigid (brittle?) visually oriented designs don’t reformat well at
all and need to be handled with zooming and panning.

Because the majority of websites weren’t designed to scale well, when mobile browsers have to guess
how to render your page they’ll generally fail safe and go with the zoom-and-pan style rendering.
The solution to this problem is to tell the browser what your design dimensions are so that it doesn’t
have to guess.

Often, Viewport tags are used only in pages that are speciically designed for small form factors,
based on browser snifing or user selection. In this case, you would see a Viewport tag that looks
something like this:

<meta name="viewport" content="width=320">

This works for mobile-speciic views but doesn’t adapt to larger sizes well.

A better solution is to design your CSS to scale well at all sizes (more on that in a second), and then
tell the browser that the Viewport is whatever the device supports. Fortunately, that’s pretty easy:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Adaptive Styles Using CSS Media Queries

Okay, you’ve told browsers that your page will look brilliant when scaled to the current device’s
screen dimensions. That’s a bold claim! How will you follow through on that promise? The answer
is CSS Media Queries.

CSS Media Queries allow you to target CSS rules at particular media (display) features. From the
W3C Media Queries documentation:

HTML4 and CSS2 currently support media-dependent style sheets tailored for
different media types. For example, a document may use sans-serif fonts when
displayed on a screen and serif fonts when printed. ‘screen’ and ‘print’ are two
media types that have been deined. Media queries extend the functionality of
media types by allowing more precise labeling of style sheets.

Mobile Support ❘ 467

A media query consists of a media type and zero or more expressions that check
for the conditions of particular media features. Among the media features that
can be used in media queries are ‘width,’ ‘height,’ and ‘color.’ By using media
queries, presentations can be tailored to a speciic range of output devices
without changing the content itself.

—http://www.w3.org/TR/css3-mediaqueries/

To summarize, whereas with CSS2 you could use target media types like screen and print, with
media queries you can target a screen display with a certain minimum or maximum width.

Remembering that CSS rules are evaluated from top to bottom, this means that you can apply gen-
eral rules at the top of your CSS ile and override them with rules speciic to smaller displays later
in your CSS, surrounded by a media query so that they won’t be applied by browsers in larger form
factor displays.

In the following very simple example, the background will be blue on displays wider than 768px and
red on displays narrower than 768px:

body {background-color:blue;}
@media only screen and (max-width: 768px) {

body {background-color:red;}
}

The preceding example uses max-width queries, so it assumes a widescreen (desktop) default,
and then applies some adjustments below that 768px cutoff. Because Bootstrap 3 is designed as a
“mobile irst” framework, it actually lips that around using min-width queries. The default CSS
rules assume a mobile device width, and the min-width media queries apply additional formatting
when displayed on a wider resolution display.

To see examples of these media queries, open /Content/bootstrap.css and search for @media.

MEDIA QUERIES: WHY STOP AT ONE?

You can use multiple media queries in your site’s CSS to ensure your site looks good
at all screen sizes, from narrow phone browsers to huge widescreen monitors and
everything in between. The http://mediaqueri.es/ site offers a gallery of sites
that show this approach to beautiful effect.
Inspecting the Bootstrap CSS shows that it has some additional support for inter-
mediate display sizes using both min-width and max-width queries: @media (min-
width: 992px) and (max-width: 1199px).

If you’ve been paying attention, you’ll have guessed that you can test the media query support in the
MVC templates out just by resizing a desktop browser narrower than 768px (see Figure 16-4), and
that guess would be correct.

468 ❘ CHAPTER 16 ADVANCED TOPICS

FIGURE 16-4

For comparison, Figure 16-5 shows that same browser window resized to just over 768px.
You can easily test this out without writing any code: Create a new MVC 5 project, run it, and
resize the browser.

Responsive Web Design with Bootstrap

As you’ve seen, adaptive layout (using media queries to apply different CSS styles at varying screen
widths) is really useful in creating a site that works well for all form factors. Adaptive layout is part
of a broader approach to optimizing a viewing experience across a range of devices, called respon-
sive web design. Responsive web design also addresses other concerns such as luid grids, which
intelligently reposition their contents based on the screen size, and image scaling. Bootstrap includes
broad support for these issues, as well as some CSS utility classes for mobile devices.

Mobile Support ❘ 469

FIGURE 16-5

The Bootstrap 3 grid system is especially useful in managing complex layouts at varying screen
sizes. It divides the screen width into twelve columns, then lets you specify how many columns a
grid element should occupy depending on the screen width:

➤ Extra-small: <768px
➤ Small: >=768px
➤ Medium: >= 992px
➤ Large: >=1200px.

470 ❘ CHAPTER 16 ADVANCED TOPICS

For example, the following HTML allocates six columns (half the grid width) per item on mobile
devices, but only four columns (one-third of the grid width) per item on larger displays:

<div class="row">
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>

</div>

You can read more about the Bootstrap grid system—and see plenty of examples—on the Bootstrap
site: http://getbootstrap.com/css/#grid

With the preceding techniques, you’re sending the same markup to every browser and using CSS
to reformat or toggle visibility of certain elements. In some cases, using the same markup is not
enough: You need to vary the markup sent to all mobile browsers. That’s where Display modes come
in handy.

Display Modes

The view selection logic in MVC 5 includes convention-based support for alternate views. The
default view engine irst looks for views with names ending in .Mobile.cshtml when the browser’s
user agent indicates a known mobile device. For example, when a desktop browser requests the
home page, the application will use the Views\Home\Index.cshtml template. However, if a mobile
browser requests the home page, and a Views\Home\Index.Mobile.cshtml template is found, it is
used instead of the desktop view. This is all handled via convention; there’s nothing to register or
conigure.

To try out this feature, create a new MVC 5 application. Make a copy of the \Views\Home\Index.
cshtml template by selecting it in the Solution Explorer and pressing Ctrl+C and then Ctrl+V.
Rename this view Index.Mobile.cshtml. The \Views\Home directory should appear as shown in
Figure 16-6.

Edit the Index.Mobile.cshtml view, perhaps changing the content in the “jumbotron” section:

<div class="jumbotron">
<h1>WELCOME, VALUED MOBILE USER!</h1>
<p class="lead">This content is only shown to mobile browsers.</p>

</div>

Run the application and view it in a mobile emulator to see the new view, as shown in Figure 16-7.

Mobile Support ❘ 471

FIGURE 16-6

Layout and Partial View Support

You can also create mobile versions of both layouts and partial view templates.
If your Views\Shared folder contains both the _Layout.cshtml and _Layout.mobile.cshtml tem-
plates, by default the application will use _Layout.mobile.cshtml during requests from mobile
browsers and _Layout.cshtml during other requests.
If a Views\Account folder contains both _SetPasswordPartial.cshtml and _
SetPasswordPartial.mobile.cshtml, the instruction @Html.Partial("~/Views/Account/_
SetPasswordPartial") will render _ SetPasswordPartial.mobile.cshtml during requests from
mobile browsers, and _ SetPasswordPartial.cshtml during other requests.

472 ❘ CHAPTER 16 ADVANCED TOPICS

FIGURE 16-7

Custom Display Modes

Additionally, you can register your own custom device modes that will be based on your own
custom criteria. For example, to register a WinPhone device mode that would serve views ending

Advanced Razor ❘ 473

with .WinPhone.cshtml to Windows Phone devices, you would use the following code in the
Application_Start method of your Global.asax:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("WinPhone")
{

ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("Windows Phone OS", StringComparison.OrdinalIgnoreCase) >= 0)

});

That’s it—there’s nothing to register or conigure. Just create views that end with .WinPhone.
cshtml, and they will be selected whenever the context condition is met.

The context condition isn’t limited to checking the browser’s user agent; there’s no requirement that
it does anything with the request context at all. You could set up different display modes based on
user cookies, a database query that determines the user’s account type, or the day of the week. It’s
completely up to you.

MVC gives you a lot of tools to provide better experiences to users on mobile browsers. The best
advice I can give you is to make a habit of testing your sites in a mobile browser. When we tested the
ASP.NET website (http://asp.net), we found that it was really dificult to navigate the site and
read the content. We were able to dramatically improve the site experience through adaptive render-
ing, and have since seen signiicantly higher mobile usage.

ADVANCED RAZOR

Chapter 3 highlights the main Razor features you’ll likely use in day-to-day work. Razor supports
some additional features which, although a little more complex to learn to use, are really powerful.
We think they’re worth the effort.

Templated Razor Delegates

The earlier Razor Layout discussion looked at one approach to providing default content for
optional layout sections that required a bit of boilerplate code. The discussion mentioned that you
could create a better approach using a feature of Razor called templated Razor delegates.

Razor has the ability to convert an inline Razor template into a delegate. The following code sample
shows an example of this:

@{
Func<dynamic, object> strongTemplate = @<strong>@item</strong>;

}

The delegate that’s generated when using a Razor template is of type Func<T, HelperResult>. In
the preceding example the type T is dynamic. The @item parameter within the template is a special
magic parameter. These delegates are allowed only one such parameter, but the template can refer-
ence that parameter as many times as it needs to.

With the above Razor delegate deined, you can now call it anywhere within your Razor view:

<div>
@strongTemplate("This is bolded.")

</div>

474 ❘ CHAPTER 16 ADVANCED TOPICS

The result is that you can write a method that accepts a Razor template as an argument value simply
by making that argument be a Func<T, HelperResult>.

Going back to the RenderSection example presented in the Layouts example in Chapter 3, let’s do
just that:

public static class RazorLayoutHelpers {
public static HelperResult RenderSection(
this WebPageBase webPage,
string name,
Func<dynamic, HelperResult> defaultContents) {
if (webPage.IsSectionDefined(name)) {
return webPage.RenderSection(name);
}
return defaultContents(null);
}

}

The method parameters include a section name as well as a Func<dynamic, HelperResult>.
Therefore, RenderSection can be called within a Razor view, as follows:

<footer>
@this.RenderSection("Footer", @<span>This is the default.</span>)

</footer>

Notice that you passed in the default content as an argument to this method using a snippet of
Razor. Also note that the code uses the this argument to call the RenderSection extension
method.

When using an extension method of a type from within that type (or a derived type of that type),
the this parameter is required to call that extension method. When you’re writing a view, it’s not
readily apparent that you’re writing code within a class, but you are. The next section explains this
and provides an example that allows you to clean up your usage of RenderSection even more.

View Compilation

Unlike many templating engines or interpreted view engines, Razor views are dynamically com-
piled at run time into classes and then executed. The compilation happens the irst time the view
is requested, which incurs at a slight one-time performance cost. The beneit is that the next time
the view is used, it’s running fully compiled code. If the content of the view changes, ASP.NET will
automatically recompile the view.

The class that a view is compiled into derives from WebViewPage, which itself derives from
WebPageBase, which you saw earlier in the section “Templated Razor Delegates.” For long-time
ASP.NET users, this kind of derivation should come as no surprise because it is similar to how an
ASP.NET Web Forms page works with its Page base class as well.

You can change the base type for Razor views to a custom class, which makes it possible for you to
add your own methods and properties to views. The base type for Razor views is deined within the

Advanced Razor ❘ 475

Web.config ile in the Views directory. The following section of Web.config contains the Razor
coniguration:

<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory,
System.Web.Mvc, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>

</pages>
</system.web.webPages.razor>

Notice the <pages> element that has the pageBaseType attribute. The value of that attribute speci-
ies the base page type for all Razor views in your application. You can change that value by replac-
ing it with your custom base class. To do so, simply write a class that derives from WebViewPage.

Let’s do just that—add a RenderSection method overload to your CustomWebViewPage class:

using System;
using System.Web.Mvc;
using System.Web.WebPages;

public abstract class CustomWebViewPage<T> : WebViewPage<T> {
public HelperResult RenderSection(string name, Func<dynamic, HelperResult>
defaultContents) {
if (IsSectionDefined(name)) {
return RenderSection(name);
}
return defaultContents(null);
}

}

Note that the class is a generic class. This is important in order to support strongly typed views. It
turns out that all views are generically typed. When no type is speciied, that type is dynamic.

After writing this class, you need to change the base page type in Web.config:

<pages pageBaseType="CustomWebViewPage">

After making this change, all the Razor views in the application will derive from
CustomWebViewPage<T> and will have the new RenderSection overload, allowing you to deine an
optional layout section with default content without requiring the this keyword:

<footer>
@RenderSection("Footer", @<span>This is the default.</span>)

</footer>

476 ❘ CHAPTER 16 ADVANCED TOPICS

NOTE To see this code as well as Layouts in action, use NuGet to install the
Wrox.ProMvc5.Views.BasePageType package into a default ASP.NET MVC 5
project, as follows:

Install-Package Wrox.ProMvc5.Views.BasePageType

After installing this package, you must change the base page type within the
Web.config ile in the Views directory to CustomWebViewPage.

The folder in the Views directory contains an example of a layout using the
method you just implemented. Press Ctrl+F5 and visit the following two URLs
to see the code in action:

➤ /example/layoutsample

➤ /example/layoutsamplemissingfooter

ADVANCED VIEW ENGINES

Scott Hanselman, community program manager at Microsoft, likes to call the view engine “just an
angle bracket generator.” In the simplest terms, that’s exactly what it is. A view engine will take an
in-memory representation of a view and turn it into whatever other format you like. Usually, this
means that you will create a cshtml ile containing markup and script, and ASP.NET MVC’s default
view engine implementation, the RazorViewEngine, will use some existing ASP.NET APIs to render
your page as HTML.

View engines aren’t limited to using cshtml pages, nor are they limited to rendering HTML. You’ll
see later how you can create alternate view engines that render output that isn’t HTML, as well as
unusual view engines that require a custom DSL (domain-speciic language) as input.

To better understand what a view engine is, let’s review the ASP.NET MVC life cycle (very simpli-
ied in Figure 16-8).

HTTP Routing Controller ViewResult ViewEngine View Response
Request

FIGURE 16-8

A lot more subsystems are involved; this igure just highlights where the view engine comes into
play—which is right after the Controller action is executed and returns a ViewResult in response
to a request.

Note here that the controller itself does not render the view; it simply prepares the data (that is, the
model) and decides which view to display by returning a ViewResult instance. As you saw earlier

Advanced View Engines ❘ 477

in this chapter, the Controller base class contains a simple convenience method, named View, that
returns a ViewResult. Under the hood, the ViewResult calls into the current view engine to render
the view.

Configuring a View Engine

As just mentioned, having alternative view engines registered for an application is possible. View
engines are conigured in Global.asax.cs. By default, there is no need to register other view
engines if you stick with just using RazorViewEngine (and the WebFormViewEngine is also regis-
tered by default).

However, if you want to replace these view engines with another, you could use the following code
in your Application_Start method:

protected void Application_Start() {
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyViewEngine());
//Other startup registration here

}

Engines is a static ViewEngineCollection that contains all registered view engines. This is
the entry point for registering view engines. You need to call the Clear method irst because
RazorViewEngine and WebFormViewEngine are included in that collection by default. Calling the
Clear method is not necessary if you want to add your custom view engine as another option in
addition to the default one, rather than replace the default view engines.

In most cases, though, registering a view engine manually is probably unnecessary if it’s available on
NuGet. For example, to use the Spark view engine, after creating a default ASP.NET MVC 5 proj-
ect, simply run the NuGet command Install-Package Spark.Web.Mvc. This adds and conigures
the Spark view engine in your project. You can quickly see it at work by renaming Index.cshtml to
Index.spark. Change the markup to the following to display the message deined in the controller:

<!DOCTYPE html>
<html>
<head>

<title>Spark Demo</title>
</head>
<body>

<h1 if="!String.IsNullOrEmpty(ViewBag.Message)">${ViewBag.Message}</h1>
<p>

This is a spark view.
</p>
</body>
</html>

The preceding snippet shows a very simple example of a Spark view. Notice the special if attribute,
which contains a Boolean expression that determines whether the element it’s applied to is dis-
played. This declarative approach to controlling markup output is a hallmark of Spark.

478 ❘ CHAPTER 16 ADVANCED TOPICS

Finding a View

The IViewEngine interface is the key interface to implement when building a custom view engine:

public interface IViewEngine {
ViewEngineResult FindPartialView(ControllerContext controllerContext,
string partialViewName, bool useCache);
ViewEngineResult FindView(ControllerContext controllerContext, string viewName,
string masterName, bool useCache);
void ReleaseView(ControllerContext controllerContext, IView view);

}

With the ViewEngineCollection, the implementation of FindView iterates through the regis-
tered view engines and calls FindView on each one, passing in the speciied view name. This is
the means by which the ViewEngineCollection can ask each view engine whether it can render a
particular view.

The FindView method returns an instance of ViewEngineResult, which encapsulates the answer to
the question, “Can this view engine render the view?” (See Table 16-1.)

TABLE 16-1: ViewEngineResult Properties

PROPERTY DESCRIPTION

View Returns the found IView instance for the specified view name. If the
view could not be located, it returns null.
ViewEngine
Returns an IViewEngine instance if a view was found; otherwise, it
SearchedLocations returns null.

Returns an IEnumerable<string> that contains all the locations
that the view engine searched.

If the IView returned is null, the view engine was not able to locate a view corresponding to the
view name. Whenever a view engine cannot locate a view, it returns the list of locations it checked.
Typically, these are ile paths for view engines that use a template ile, but they could be something
else entirely, such as database locations for view engines that store views in a database. These loca-
tion strings are opaque to MVC itself; it uses them only to display a helpful error message to the
developer.

Note that the FindPartialView method works in the same way as FindView, except that it focuses
on inding a partial view. It is quite common for view engines to treat views and partial views differ-
ently. For example, some view engines automatically attach a master view (or layout) to the current
view by convention. It’s important for that view engine to know whether it’s being asked for a full
view or a partial view; otherwise, every partial view might have the master layout surrounding it.

Advanced View Engines ❘ 479

The View Itself

The IView interface is the second interface you need to implement when implementing a custom
view engine. Fortunately, it is quite simple, containing a single method:

public interface IView {
void Render(ViewContext viewContext, TextWriter writer);

}

Custom views are supplied with a ViewContext instance, which provides the information that might
be needed by a custom view engine, along with a TextWriter instance. The view is expected to con-
sume the data in the ViewContext (such as the view data and model) and then call methods of the
TextWriter instance to render the output.

Table 16-2 lists the properties exposed by ViewContext.

TABLE 16-2: ViewContext Properties

PROPERTY DESCRIPTION

HttpContext An instance of HttpContextBase, which provides access
to the ASP.NET intrinsic objects, such as Server, Session,
Controller Request, Response.
RouteData
ViewData An instance of ControllerBase, which provides access to
TempData the controller, making the call to the view engine.

View An instance of RouteData, which provides access to the
ClientValidationEnabled route values for the current request.
FormContext
FormIdGenerator An instance of ViewDataDictionary containing the data
passed from the controller to the view.

An instance of TempDataDictionary containing data
passed to the view by the controller in a special one-
request-only cache.

An instance of IView, which is the view being rendered.

Boolean value indicating whether client validation has been
enabled for the view.

Contains information about the form, used in client-side
validation.

Allows you to override how forms are named (“form0”-style
by default).

continues

480 ❘ CHAPTER 16 ADVANCED TOPICS

TABLE 16-2 (continued)

PROPERTY DESCRIPTION

IsChildAction Boolean value indicating whether the action is being
displayed as a result of a call to Html.Action or Html.
ParentActionViewContext RenderAction.
Writer
When IsChildAction is true, contains the ViewContext
UnobtrusiveJavaScriptEnabled of this view’s parent view.

HtmlTextWriter to use for HTML helpers that don’t return
strings (that is, BeginForm), so that you remain compatible
with non–Web Forms view engines.

This property determines whether an unobtrusive approach
to client validation and Ajax should be used. When true,
rather than emitting script blocks into the markup, HTML
5 data-* attributes are emitted by the helpers, which the
unobtrusive scripts use as a means of attaching behavior to
the markup.

Not every view needs access to all these properties to render a view, but it’s good to know they are
there when needed.

Alternative View Engines

When working with ASP.NET MVC for the irst time, you’re likely to use the view engine that
comes with ASP.NET MVC: the RazorViewEngine. The many advantages to this include that it

➤ Is the default
➤ Has clean, lightweight syntax
➤ Has layouts
➤ Has HTML encoded by default
➤ Has support for scripting with C#/VB
➤ Has IntelliSense support in Visual Studio

Sometimes, however, you might want to use a different view engine—for example, when you:
➤ Want to use a different language, such as Ruby or Python
➤ Render non-HTML output, such as graphics, PDFs, RSS, and the like
➤ Have legacy templates using another format

Advanced View Engines ❘ 481

Several third-party view engines are available at the time of this writing. Table 16-3 lists some of the
more well-known view engines, but there are likely many others we’ve never heard of.

TABLE 16-3: Alternative View Engines

VIEW ENGINE DESCRIPTION

Spark Spark (https://github.com/SparkViewEngine) is the brainchild of Louis
DeJardin (now a Microsoft employee) with support for both MonoRail and ASP.
NHaml NET MVC. It is of note because it blurs the line between markup and code
using a very declarative syntax for rendering views. Spark continues to add
Brail innovative features, including support for the Jade templating language first
popularized on Node.js.
StringTemplate
NHaml (hosted on GitHub at https://github.com/NHaml/NHaml), created
Nustache by Andrew Peters and released on his blog in December 2007, is a port of the
popular Ruby on Rails Haml View engine. It’s a very terse DSL used to describe
Parrot the structure of XHTML with a minimum of characters.
JavaScript
View Engines Brail (part of the MvcContrib project, http://mvccontrib.org) is interesting
(JSVE) for its use of the Boo Language. Boo is an object-oriented statically typed lan-
guage for the CLR with a Python language style to it, such as significant white
space.

StringTemplate (hosted at Google code, http://code.google.com/p/
string-template-view-engine-mvc) is a lightweight templating engine
that is interpreted rather than compiled. It’s based on the Java StringTemplate
engine.

Nustache (https://github.com/jdiamond/Nustache) is a .NET implementa-
tion of the popular Mustache templating language (so named because it uses
curly braces that look like sideways mustaches). Nustache is known for being a
“logic-less” templating system because it intentionally doesn’t support any con-
trol flow statements. The Nustache project includes an MVC view engine.

Parrot (http://thisisparrot.com) is an interesting new view engine with a
CSS-inspired view syntax, good support for enumerables and nested objects,
and an extensible rendering system.

JavaScript View Engines (https://github.com/Buildstarted/Javascript.
ViewEngines) is another new view engine from Ben Dornis, creator of Parrot.
It's an extensible engine for common JavaScript templating systems like
Mustache and Handlebars. The advantage of this common implementation is
that adding support for another templating system just requires adding the
JavaScript file to your Scripts directory and registering it in the JavaScript.
ViewEngines.js file.

482 ❘ CHAPTER 16 ADVANCED TOPICS

New View Engine or New ActionResult?

We are often asked when someone should create a custom view engine as opposed to a new
ActionResult type. For example, suppose that you want to return objects in a custom XML for-
mat. Should you write a custom view engine or a new MyCustomXmlFormatActionResult?

The general rule for choosing between one and the other is whether it makes sense to have some sort
of template ile that guides how the markup is rendered. If there’s only one way to convert an object
to the output format, then writing a custom ActionResult type makes more sense.

For example, the ASP.NET MVC Framework includes a JsonResult, which serializes an object to
JSON syntax. In general, there’s only one way to serialize an object to JSON. You wouldn’t change
the serialization of the same object to JSON according to which action method or view is being
returned. Serialization is generally not controlled via a template.

However, suppose that you wanted to use XSLT to transform XML into HTML. You might have
multiple ways to transform the same XML into HTML, depending on which action you’re invoking.
In this case, you would create an XsltViewEngine, which uses XSLT iles as the view templates.

ADVANCED SCAFFOLDING

Chapter 4 overviewed the MVC 5 use of scaffolded views, which make it easy to create the control-
ler and views to support, create, read, update, and delete functionality just by setting options in the
Add Controller dialog. In that discussion, we noted that this scaffolding system is extensible. This
section describes a few approaches for extending the default scaffolding experience.

Introducing ASP.NET Scaffolding

Although scaffolding has been a part of MVC since the irst release, it was limited to MVC projects.
With the release of Visual Studio 2013, scaffolding has been rewritten as a new feature called ASP.
NET Scaffolding.

As the name implies, ASP.NET Scaffolding is now available in any ASP.NET application, not just in
MVC projects. This means that you can add any of the default scaffold templates in any ASP.NET
project; for example, you can add scaffolded MVC controllers and views to a project created with
the ASP.NET Web Forms or Empty templates.

The previous MVC scaffolding system allowed for some customization, but the new ASP.NET
Scaffolding system was designed with customization in mind. Two methods of customization are
available:

➤ Scaffold template customization allows you to alter the code generated using the existing
scaffolders.

➤ Custom scaffolders allow you to add new scaffolds to the Add New Scaffold dialog.

Advanced Scaffolding ❘ 483

Customizing Scaffold Templates

The default scaffolders generate code using the Text Template Transformation Toolkit, commonly
referred to as T4. T4 is a code generation engine integrated with Visual Studio. As the name implies,
the template format is text based, so templates are relatively easy to edit. T4 templates contain a mix
of string literals and C# code, using a syntax that’s pretty similar to the original Web Forms view
syntax. You can ind out more information on the T4 system and syntax at http://msdn.micro-
soft.com/en-us/library/bb126445.aspx.

NOTE Visual Studio just displays T4 templates (iles with a .t4 extension) as
plain text. A few Visual Studio extensions exist that add enhanced T4 support to
Visual Studio 2013. These commonly add syntax highlighting and IntelliSense,
as well as a variety of other useful features. I recommend searching the Visual
Studio Gallery (http://visualstudiogallery.msdn.microsoft.com/) for T4
and trying a few.

Assuming you have Visual Studio 2013 installed in C:\Program Files (x86)\Microsoft Visual
Studio 12.0, the default templates is found in C:\Program Files (x86)\Microsoft Visual
Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates.

Not modifying the base templates is best, though, because they will affect all projects on your
computer. The scaffolding system allows you to override the scaffold templates on a per-project
basis, which also has the advantage of allowing you to check the modiied scaffold templates in to
source control.

ASP.NET Scaffolding looks for a CodeTemplate folder in your project, so if you want to customize
them, you can just create a new CodeTemplates directory in the root of your project and copy in
the aforementioned templates. Note that the templates include both C# and VB.NET versions of the
templates, so you’ll want to delete the iles for the language that don’t apply.

A better way to add the scaffold templates to your application is via a Visual Studio extension
called SideWafle. The SideWafle extension makes it really easy to add snippets, Project templates,
and Item templates to your project. The SideWafle website (http://sidewaffle.com) has more
information, a list of available templates, and a link to download the extension. After installing the
SideWafle extension, you can add the CodeTemplates directory to any project using Add / New
Item dialog, selecting the Web / SideWafle group, and selecting the ASP.NET Scaffolding T4 iles
template as shown in Figure 16-9.

This option adds a CodeTemplates folder to your application with all the standard MVC scaffold
templates. From here, you can just double-click a template and modify it, as shown in Figure 16-10.

484 ❘ CHAPTER 16 ADVANCED TOPICS

FIGURE 16-9
FIGURE 16-10

Advanced Scaffolding ❘ 485

Adding a new scaffold template is as simple as copying and pasting one of the existing templates,
renaming it to whatever you want to show up in the dialog, and modifying the template code.
For instance, if you commonly show a delete success view after a delete action, you can copy
one of the templates in /CodeTemplates/MvcView and rename it to /CodeTemplates/MvcView/
DeleteSuccess.cs.t4. Make any code changes to this new template and save it. Now, any time
you select the standard Add / View dialog, DeleteSuccess will appear in the list, as shown in
Figure 16-11.

FIGURE 16-11

Customizing the scaffold templates is an easy, low-risk way to make your whole team more
productive. If you ind that the custom templates aren’t work working for you, just delete the
CodeTemplates directory from your project to return to the default behavior.

Custom Scaffolders

The scaffolding system provides for extensive customization, far beyond what you can do by cus-
tomizing the scaffold templates. Custom scaffolders enable any Visual Studio extension (VSIX) to
code against the Scaffolding API surface and have their scaffolds added to the Add / New Scaffolded

486 ❘ CHAPTER 16 ADVANCED TOPICS

Item dialog. This feature is even more powerful when you remember that the ASP.NET Scaffolding
system works in any ASP.NET project.

As you would expect, this level of customization requires you to put in some effort. A full walk-
through is available on the .NET Web Development and Tools blog: http://blogs.msdn.com/b/
webdev/archive/2014/04/03/creating-a-custom-scaffolder-for-visual-studio.aspx.

Here’s a high-level overview of the steps:

1. If you don’t have it already, install the SideWafle extension for Visual Studio 2013.
2. Create a new project in Visual Studio.
3. In the New Project dialog, select the Templates ➪ Extensibility ➪ SideWafle node,

then select the BasicScaffolder template. This creates two projects: a VSIX project and a
CodeGenerator class library.

4. Modify the metadata in both the VSIX and code generator to customize how the extension

will appear when published and instantiated.

5. Modify the dialog that is presented to users when they invoke your scaffolder. This dialog is

where you accept user input and option selections before the scaffolder executes.

6. Write the actual GenerateCode method. This does the actual work of generating the code.

Fortunately, the Scaffolding API provides utility methods for just about everything you
would need to do, including adding iles and folders, generating code using T4 templates, and
adding NuGet packages.

7. Test and build the solution. This creates a VSIX ile.
8. If you desire, you can deploy your VSIX to the Visual Studio Gallery to share it with the rest

of the world.

A great way to learn about writing custom scaffolders is by looking at the source code for the Web
Forms scaffolder: https://github.com/Superexpert/WebFormsScaffolding.

ADVANCED ROUTING

As mentioned at the end of Chapter 9, routing is simple to learn yet challenging to master. This
section describes a few advanced tips Phil recommends to simplify some otherwise tricky routing
scenarios.

RouteMagic

In Chapter 9, we mentioned the RouteMagic project, which is an open source project available on
GitHub at https://github.com/Haacked/RouteMagic. You can install this package with the
following command:

Install-Package RouteMagic.Mvc

Advanced Routing ❘ 487

This project is also available as a NuGet package appropriately named RouteMagic. RouteMagic is
a pet project of Phil Haack, one of the authors of this book, and provides useful extensions to ASP.
NET Routing that go above and beyond what’s included “in the box.”

One useful extension included in the RouteMagic package offers support for redirect routes. As
noted usability expert Jakob Nielsen has recommended, “persistent URLs don’t change,” and redi-
rect routes can help you support that.

One of the beneits of routing is that you can change your URL structure all you want during
development by manipulating your routes. When you do so, all the URLs in your site are updated
automatically to be correct, which is a nice feature. But once you deploy your site to the public, this
feature becomes a detriment, as others start to link to the URLs you’ve deployed. You don’t want to
change a route at this point and break every incoming URL—unless you properly redirect.

After installing RouteMagic, you’ll be able to write redirect routes that take in an old route and
redirect it to a new route, as follows:

var newRoute = routes.MapRoute("new", "bar/{controller}/{id}/{action}");
routes.Redirect(r => r.MapRoute("oldRoute",

"foo/{controller}/{action}/{id}")
).To(newRoute);

For more information on RouteMagic, visit the RouteMagic repository at https://github.com/
Haacked/RouteMagic. We think you’ll ind it to be an indispensable tool for your routing needs.

Editable Routes

In general, after you deploy your ASP.NET MVC application, you can’t change the routes for your
application without recompiling the application and redeploying the assembly where your routes are
deined.

This is partly by design because routes are generally considered application code, and should have
associated unit tests to verify that the routes are correct. A misconigured route could seriously tank
your application.

Having said that, many situations exist in which the ability to change an application’s routes with-
out having to recompile the application comes in very handy, such as in a highly lexible content
management system or blog engine.

The RouteMagic project just mentioned includes support for routes that can be modiied while the
application is running. Begin by adding a new Routes class to the App_Start directory of an ASP.
NET MVC 5 application (see Figure 16-12).

Next, use Visual Studio’s Properties dialog to mark the ile’s Build Action as “Content” so that it’s
not compiled into the application, as shown in Figure 16-13.

488 ❘ CHAPTER 16 ADVANCED TOPICS

FIGURE 16-12

FIGURE 16-13

Setting the Build Action to “Content” intentionally excludes the Routes.cs ile from build-time
compilation because we want it to be compiled dynamically at run time. Following is the code for
Routes.cs. (Don’t worry about entering this code manually; it’s provided as a NuGet package at the
end of this section.)

Advanced Routing ❘ 489

using System.Web.Mvc;
using System.Web.Routing;
using RouteMagic;
public class Routes : IRouteRegistrar
{

public void RegisterRoutes(RouteCollection routes)
{

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(

name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",

action = "Index",
id = UrlParameter.Optional }
);
}
}

NOTE The RouteMagic compilation system will be looking for a class named
Routes with no namespace. If you use a different class name or forget to remove
the namespace, the routes won’t be registered.

The Routes class implements an interface named IRouteRegistrar that is deined in the
RouteMagic assembly. This interface deines one method, RegisterRoutes.

Next, you’ll change the route registration in App_Start/RouteConfig.cs to use a new extension
method to register the routes:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using RouteMagic;
namespace Wrox.ProMvc5.EditableRoutes
{

public class RouteConfig
{

public static void RegisterRoutes(RouteCollection routes)
{

RouteTable.Routes.RegisterRoutes("~/App_Start/Routes.cs");
}
}
}

490 ❘ CHAPTER 16 ADVANCED TOPICS

With this in place, you can now change routes within the Routes.cs ile in the App_Start directory
after you’ve deployed the application without recompiling your application.

To see this in action, you can run the application and notice the standard home page comes up.
Then, without stopping the application, alter the default route so the Account controller and Login
action are set as route defaults:

using System.Web.Mvc;
using System.Web.Routing;
using RouteMagic;

public class Routes : IRouteRegistrar
{

public void RegisterRoutes(RouteCollection routes)
{

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(

name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account",

action = "Login",
id = UrlParameter.Optional }
);
}
}

When you refresh the page, you’ll see that the Login view is now displayed.

EDITABLE ROUTES: THE GORY DETAILS

The previous section explains all you need to know to use editable routes. In case
you’re interested, here’s how it works.

Usage seems simple enough, but that’s because we’ve hidden all the magic in an
extension method on RouteCollection. This method uses two tricks that allow
us to dynamically generate the routing code in medium trust, without causing an
application restart:

1. We use the ASP.NET BuildManager to dynamically create an assembly from

the Routes.cs ile. From that assembly, we can create an instance of the type
Routes and cast it to IRouteHandler.

2. We use the ASP.NET Cache to get a notiication of when the Routes.cs ile

changes, so we’ll know it needs to be rebuilt. The ASP.NET Cache allows us
to set a cache dependency on a ile and a method to call when the ile changes
(invalidating the cache).

Advanced Routing ❘ 491

Here’s the code that RouteMagic uses to add a cache dependency pointing to
Routes.cs and a callback method that will reload the routes when Routes.cs is
changed:

using System;
using System.Web.Compilation;
using System.Web.Routing;
using RouteMagic.Internals;
namespace RouteMagic
{

public static class RouteRegistrationExtensions
{

public static void RegisterRoutes
(this RouteCollection routes,
string virtualPath)

{
if (String.IsNullOrEmpty(virtualPath))
{
throw new ArgumentNullException("virtualPath");
}
routes.ReloadRoutes(virtualPath);
ConfigFileChangeNotifier.Listen(virtualPath,
routes.ReloadRoutes);

}
static void ReloadRoutes(this RouteCollection routes,

string virtualPath)
{

var assembly = BuildManager.GetCompiledAssembly(
virtualPath);

var registrar = assembly.CreateInstance("Routes")
as IRouteRegistrar;

using (routes.GetWriteLock())
{

routes.Clear();
if (registrar != null)
{

registrar.RegisterRoutes(routes);
}
}
}
}
}

One more interesting bit: The ile change notiications are implemented using
the ConfigFileChangeNotifier from ASP.NET team member David Ebbo’s
work on the ASP.NET Dynamic Data scaffolding system. For that code and
more technical background, see Phil Haack’s post at http://haacked.com/
archive/2010/01/17/editable-routes.aspx.

492 ❘ CHAPTER 16 ADVANCED TOPICS

ADVANCED TEMPLATES

Chapter 5 introduced templated helpers. The templated helpers are the subset of HTML helpers
including EditorFor and DisplayFor, and they are called the templated helpers because they ren-
der HTML using model metadata and templates. To jog your memory, imagine the following Price
property on a model object:

public decimal Price { get; set; }

You can use the EditorFor helper to build an input for the Price property.
@Html.EditorFor(m=>m.Price)

The resulting HTML will look like the following:

<input class="text-box single-line" id="Price"
name="Price" type="text" value="8.99" />

You’ve seen how you can change the output of the helper by adding model metadata in the form
of data annotation attributes like Display and DisplayFormat. What you haven’t seen yet is how
to change the output by overriding the default MVC templates with your own custom templates.
Custom templates are powerful and easy, but before building any custom templates we’ll show you
how the built-in templates work.

The Default Templates

The MVC framework includes a set of built-in templates the templated helpers will use when con-
structing HTML. Each helper will select a template based on information about the model — both
the model type and model metadata. For example, imagine a bool property named IsDiscounted.

public bool IsDiscounted { get; set; }

Again, you can use EditorFor to build an input for the property.
@Html.EditorFor(m=>m.IsDiscounted)

This time, the helper renders a checkbox input (compare this to the editor for the Price property
earlier, which used a text input).

<input class="check-box" id="IsDiscounted" name="IsDiscounted"
type="checkbox" value="true" />

<input name="IsDiscounted" type="hidden" value="false" />

Actually, the helper emits two input tags (we discussed the reason for the second, hidden input in the
“Html.CheckBox” section of Chapter 5), but the primary difference in output is that the EditorFor
helper used a different template for a bool property than it did for the decimal property. Providing
a checkbox input for a bool value and a more freeform text entry for a decimal makes sense.

Template Definitions

You can think of templates as similar to partial views — they take a model parameter and render
HTML. Unless the model metadata indicates otherwise, the templated helpers select a template

Advanced Templates ❘ 493

based on the type name of the value it is rendering. When you ask EditorFor to render a property
of type System.Boolean (like IsDiscounted), it uses the template named Boolean. When you ask
EditorFor to render a property of type System.Decimal (like Price), it uses the template named
Decimal. You’ll see more details about template selection in the next section.

DEFAULT TEMPLATES

You might be wondering whether you can look at MVC’s default templates, either
to learn how they work or to make a minor tweak. Unfortunately, the default
templates are implemented directly in code in System.Web.Mvc.dll rather that in
template format.

The following examples showing Decimal and Boolean templates were created from
some samples shipped with the ASP.NET MVC 3 Futures library, and then translated
from Web Forms view engine syntax to Razor syntax. There’s no oficial source for
current default templates, but you can get a good idea of what they’re doing by read-
ing the source code: http://aspnetwebstack.codeplex.com/SourceControl/
latest#src/System.Web.Mvc/Html/DefaultEditorTemplates.cs.

If the default Decimal template were available in Razor syntax, it would look something like this:

@using System.Globalization

@Html.TextBox("", FormattedValue, new { @class = "text-box single-line" })

@functions
{

private object FormattedValue {
get {
if (ViewData.TemplateInfo.FormattedModelValue ==
ViewData.ModelMetadata.Model) {
return String.Format(
CultureInfo.CurrentCulture,
"{0:0.00}", ViewData.ModelMetadata.Model
);
}
return ViewData.TemplateInfo.FormattedModelValue;
}

}
}

The template uses the TextBox helper to create an input element (of type text) with a formatted
model value. Notice the template also uses information from the ModelMetadata and TemplateInfo
properties of ViewData. ViewData contains a wealth of information you might need inside a
template, and even the simplest of the templates, the String template, uses ViewData.

@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { @class = "text-box single-line" })

494 ❘ CHAPTER 16 ADVANCED TOPICS

The TemplateInfo property of ViewData gives you access to a FormattedModelValue property.
The value of this property is either the properly formatted model value as a string (based on the
format strings in ModelMetadata) or the original raw model value (if there is no format string speci-
ied). ViewData also grants access to model metadata. You can see model metadata at work in a
Boolean editor template (the template the framework uses for the IsDiscounted property you saw
earlier).

@using System.Globalization

@if (ViewData.ModelMetadata.IsNullableValueType) {
@Html.DropDownList("", TriStateValues,
new { @class = "list-box tri-state" })

} else {
@Html.CheckBox("", Value ?? false,
new { @class = "check-box" })

}

@functions {
private List<SelectListItem> TriStateValues {
get {
return new List<SelectListItem> {
new SelectListItem {
Text = "Not Set", Value = String.Empty,
Selected = !Value.HasValue
},
new SelectListItem {
Text = "True", Value = "true",
Selected = Value.HasValue && Value.Value
},
new SelectListItem {
Text = "False", Value = "false",
Selected = Value.HasValue && !Value.Value
},
};
}
}
private bool? Value {
get {
if (ViewData.Model == null) {
return null;
}
return Convert.ToBoolean(ViewData.Model,
CultureInfo.InvariantCulture);
}
}

}

As you can see from the previous code, this Boolean template builds a different editor for nullable
Boolean properties (using a drop-down list) versus a non-nullable property (a checkbox). Most of
the work here is building the list of items to display in the drop-down list.

Advanced Templates ❘ 495

Template Selection

It should be clear that if the framework selects a template based on a model’s type name, then a
decimal property renders with the Decimal template. But what about types that don’t have a default
template deined in System.Web.Mvc.Html.DefaultEditorTemplates—types such as Int32 and
DateTime?

Before checking for a template matching the type name, the framework irst checks model metadata
to see whether a template hint exists. You can specify the name of a template to use with the UIHint
data annotation attribute—you’ll see an example later. The DataType attribute can also inluence
template selection.

[DataType(DataType.MultilineText)]
public string Description { get; set; }

The framework will use the MultilineText template when rendering the Description property
shown in the preceding code. A DataType of Password also has a default template.

If the framework doesn’t ind a matching template based on metadata, it falls back to the type name.
A String uses the String template; a Decimal uses the Decimal template. For types that don’t have a
matching template, the framework uses the String template if the object is not a complex type, or the
Collection template if the object is a collection (like an array or list). The Object template renders
all complex objects. For example, using EditorForModel helper on the Music Store’s Album model
would result in the Object template taking charge. The Object template is a sophisticated template
that uses relection and metadata to create HTML for the right properties on a model.

if (ViewData.TemplateInfo.TemplateDepth > 1) {
if (Model == null) {
@ViewData.ModelMetadata.NullDisplayText
}
else {
@ViewData.ModelMetadata.SimpleDisplayText
}

}
else {

foreach (var prop in ViewData.ModelMetadata
.Properties
.Where(pm => ShouldShow(pm))) {

if (prop.HideSurroundingHtml) {
@Html.Editor(prop.PropertyName)

}
else {

if (!String.IsNullOrEmpty(
Html.Label(prop.PropertyName).ToHtmlString())) {
<div class="editor-label">
@Html.Label(prop.PropertyName)
</div>

}
<div class="editor-field">

496 ❘ CHAPTER 16 ADVANCED TOPICS

@Html.Editor(prop.PropertyName)
@Html.ValidationMessage(prop.PropertyName, "*")
</div>
}
}
}
@functions {
bool ShouldShow(ModelMetadata metadata) {
return metadata.ShowForEdit
&& !metadata.IsComplexType
&& !ViewData.TemplateInfo.Visited(metadata);
}
}

The opening if statement in the Object template ensures the template only traverses one level into
an object. In other words, for a complex object with a complex property, the Object template shows
only a simple summary of the complex property (using NullDisplayText or SimpleDisplayText
from model metadata).

If you don’t like the behavior of the Object template, or the behavior of any of the built-in templates,
then you can deine your own templates and override the defaults.

Custom Templates

Custom templates will live in a DisplayTemplates or EditorTemplates folder. The MVC frame-
work follows a familiar set of rules when it resolves the path to a template. First, it looks underneath
the folder associated with a speciic controller’s views, but then it also looks underneath the Views/
Shared folder to see whether any custom templates exist. The framework looks for templates associ-
ated with every view engine conigured into the application (so by default, the framework looks for
templates with .aspx, .ascx, and .cshtml extensions).

As an example, say you want to build a custom Object template, but only make it available to
views associated with the MVC Music Store’s StoreManager controller. In that case, you create an
EditorTemplate underneath the Views/StoreManager folder and create a new Razor view named
Object.cshtml (see Figure 16-14).

You can do many interesting things with custom templates. Perhaps you don’t like the default styles
associated with a text input (text-box single-line). You could build your own String editor
template with your own styles and place it in the Shared\EditorTemplates folder to make it work
throughout the entire application.

Another example is to emit custom data- attributes for client scripting (you saw data- attributes in
Chapter 8). For example, say you wanted to hook up a jQuery UI Datepicker widget with every edi-
tor for a DateTime property. The framework will render a DateTime property editor using the String
template by default, but you can create a DateTime template to override this behavior, because the
framework helper looks for a template named DateTime when it renders a DateTime value with
templates.

@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { @class = "text-box single-line",
data_datepicker="true"
})

Advanced Templates ❘ 497

FIGURE 16-14

498 ❘ CHAPTER 16 ADVANCED TOPICS

You could place the preceding code inside a ile named DateTime.cshtml, and place the ile inside
the Shared\EditorTemplates folder. Then, all you need to add a Datepicker to every DateTime
property editor is a small bit of client script (be sure to include the jQuery UI scripts and stylesheets
as you saw in Chapter 8, too).

$(function () {
$(":input[data-datepicker=true]").datepicker();

});

Now imagine you didn’t want a Datepicker available for every DateTime editor, but only a hand-
ful of special cases. In that case, you could name the template ile SpecialDateTime.cshtml. The
framework won’t select this template for a DateTime model unless you specify the template name.
You can specify the name using the EditorFor helper (in this case rendering a DateTime property
named ReleaseDate).

@Html.EditorFor(m => m.ReleaseDate, "SpecialDateTime")

Alternatively, you can place a UIHint attribute on the ReleaseDate property itself.
[UIHint("SpecialDateTime")]
public DateTime ReleaseDate { get; set; }

Custom templates are a powerful mechanism you can use to reduce the amount of code you need to
write for an application. By placing your standard conventions inside of templates, you can make
sweeping changes in an application by changing just a single ile.

CUSTOM TEMPLATES ON NUGET

Some very useful EditorTemplates collections are available as NuGet packages.
For example, one titled Html5EditorTemplates adds support for HTML5 form
elements. You can ind them by searching on the EditorTemplates tag: http://
www.nuget.org/packages?q=Tags%3A%22EditorTemplates%22.

ADVANCED CONTROLLERS

As the workhorse of the ASP.NET MVC stack, it’s no surprise that the controller has a lot of
advanced features that were way beyond the scope of Chapter 2. In this section, you’ll learn both
how the controller internals work and how you can use it in some advanced scenarios.

Defining the Controller: The IController Interface

Now that you have the basics down, we’ll take a more structured look at exactly how controllers are
deined and used. Up to this point, we’ve kept things simple by focusing on what a controller does;
now it’s time to look at what a controller is. To do that, you’ll need to understand the IController
interface. As discussed in Chapter 1, among the core focuses of ASP.NET MVC are extensibility

Advanced Controllers ❘ 499

and lexibility. When building software with these focuses, leveraging abstraction as much as pos-
sible by using interfaces is important.

For a class to be a controller in ASP.NET MVC, it must at minimum implement the IController
interface, and by convention the name of the class must end with the sufix Controller. The nam-
ing convention is actually quite important — and you’ll ind that many of these small rules are in
play with ASP.NET MVC, which will make your life just a little bit easier by not making you deine
coniguration settings and attributes. Ironically, the IController interface is quite simple, given the
power it is abstracting:

public interface IController
{

void Execute(RequestContext requestContext);
}

It’s a simple process, really: When a request comes in, the Routing system identiies a controller, and
it calls the Execute method.

The point of the IController interface is to provide a very simple starting point for anyone who
wants to hook his or her own controller framework into ASP.NET MVC. The Controller class,
which is covered later in this chapter, layers much more interesting behavior on top of this interface.
This is a common extensibility pattern within ASP.NET.

For example, if you’re familiar with HTTP handlers, you might have noticed that the IController
interface looks very similar to IHttpHandler:

public interface IHttpHandler
{

void ProcessRequest(HttpContext context);
bool IsReusable { get; }
}

Ignoring the IsReusable property for a moment, IController and IHttpHandler are pretty
much equivalent in terms of responsibility. The IController.Execute and IHttpHandler.
ProcessRequest methods both respond to a request and write some output to a response. The main
difference between the two is the amount of contextual information provided to the method. The
IController.Execute method receives an instance of RequestContext, which includes not just the
HttpContext but also other information relevant to a request for ASP.NET MVC.

The Page class, which is probably the class most familiar to ASP.NET Web Forms developers
because it is the default base class for an ASPX page, also implements IHttpHandler.

The ControllerBase Abstract Base Class

Implementing IController is pretty easy, as you’ve seen, but really all it’s doing is providing a facil-
ity for Routing to ind your controller and call Execute. This is the most basic hook into the system
that you could ask for, but overall it provides little value to the controller you’re writing. This may
be a good thing to you — many custom tool developers don’t like it when a system they’re trying to
customize imposes a lot of restrictions. Others may like to work a bit closer with the API, and for
that there is ControllerBase.

500 ❘ CHAPTER 16 ADVANCED TOPICS

PRODUCT TEAM ASIDE

Back in the early days the ASP.NET MVC product team debated removing the
IController interface completely. Developers who wanted to implement that inter-
face could use their own implementation of MvcHandler instead, which decidedly
handles a lot of the core execution mechanics based on the request coming in from
Routing.
We decided to leave it in, however, because other features of the ASP.NET MVC
framework (IControllerFactory and ControllerBuilder) can work with the
interface directly — which provides added value to developers.

The ControllerBase class is an abstract base class that layers a bit more API surface on top of the
IController interface. It provides the TempData and ViewData properties (which are ways of send-
ing data to a view, discussed in Chapter 3). The Execute method of ControllerBase is responsible
for creating the ControllerContext, which provides the MVC-speciic context for the current
request much the same way that an instance of HttpContext provides the context for ASP.NET in
general (providing request and response, URL, and server information, among elements).

This base class is still very lightweight and enables developers to provide extremely customized
implementations for their own controllers, while beneiting from the action ilter infrastructure in
ASP.NET MVC (action ilters allow for iltering and working with request/response data, as dis-
cussed in Chapter 13). What it doesn’t provide is the ability to convert actions into method calls.
That’s where the Controller class comes in.

The Controller Class and Actions

In theory, you could build an entire site with classes that implement ControllerBase or
IController, and it would work. Routing would look for an IController by name and then call
Execute, and you would have yourself a very, very basic website.

This approach, however, is akin to working with ASP.NET using raw IHttpHandlers — it would
work, but you’re left to reinvent the wheel and plumb the core framework logic yourself.

Interestingly, ASP.NET MVC itself is layered on top of HTTP handlers, as you’ll see later, and over-
all there was no need to make internal plumbing changes to ASP.NET to implement MVC. Instead,
the ASP.NET MVC team layered this new framework on top of existing ASP.NET extensibility
points.

The standard approach to writing a controller is to have it inherit from the System.Web.Mvc
.Controller abstract base class, which implements the ControllerBase base class, and thus the

Advanced Controllers ❘ 501

IController interface. The Controller class is intended to serve as the base class for all control-
lers because it provides a lot of nice behaviors to controllers that derive from it.
Figure 16-15 shows the relationship between IController, ControllerBase, the Controller
abstract base class, and the two controllers that are included in a default ASP.NET MVC 5
application.

FIGURE 16-15

502 ❘ CHAPTER 16 ADVANCED TOPICS

Action Methods

All public methods of a class that derive from Controller are action methods, which are potentially
callable via an HTTP request. Rather than one monolithic implementation of Execute, you can fac-
tor your controller into action methods, each of which responds to a speciic user input.

PRODUCT TEAM ASIDE

Upon reading that every public method of your Controller class is publicly call-
able from the Web, you might have a gut reaction concerning the security of such
an approach. The product team had a lot of internal and external debate concern-
ing this.

Originally, each action method required that an attribute,
ControllerActionAttribute, be applied to each callable method. However, many
felt this violated the DRY principle (Don’t Repeat Yourself). It turns out that the
concern over these methods being web-callable has to do with a disagreement over
what it means to opt in.

As far as the product team is concerned, multiple levels of opting in exist before
a method is web-callable. The irst level that you need to have opted in to is an
ASP.NET MVC project. If you add a public Controller class to a standard ASP.
NET Web Application project, that class is not going to suddenly be web-callable
(although adding such a class to an ASP.NET MVC project is likely to make it
callable). You would still need to deine a route with a route handler (such as the
MvcRouteHandler) that corresponds to that class.

The general consensus here is that by inheriting from Controller, you’ve opted in
to this behavior. You can’t do that by accident. Even if you did, you would still have
to deine routes that correspond to that class.

The ActionResult

As mentioned before, the purpose of the controller within the MVC pattern is to respond to user
input. In ASP.NET MVC, the action method is the granular unit of response to user input. The
action method is ultimately responsible for handling a user request and outputting the response that
is displayed to the user, which is typically HTML.

The pattern that an action method follows is to do whatever work is asked of it, and at the end,
return an instance of a type that derives from the ActionResult abstract base class.

Taking a quick look at the source for the ActionResult abstract base class, you see:

public abstract class ActionResult
{

public abstract void ExecuteResult(ControllerContext context);
}

Advanced Controllers ❘ 503

Notice that the class contains a single method, ExecuteResult. If you’re familiar with the
Command Pattern, this should look familiar to you. Action results represent commands that your
action method wants the framework to perform on its behalf.

Action results generally handle framework-level work, while the action method handles your appli-
cation logic. For example, when a request comes in to display a list of products, your action method
will query the database and put together a list of the appropriate products to show. Perhaps it needs
to perform some iltering based on business rules within your app. At this point, your action method
is completely focused on application logic.

However, when the method is ready to display the list of products to the user, you may not want
your code, which is focused on view logic, to have to worry about implementation details provided
by the framework, such as writing to the HTTP response directly. Perhaps you have a template
deined that knows how to format a collection of products as HTML. You would rather not have
that information encapsulated in the action method because it would violate the separation of con-
cerns the authors have so carefully cultivated up until this point.

One technique you have at your disposal is to have the action method return a ViewResult (which
derives from ActionResult) and give the data to that instance, and then return that instance. At that
point, your action method is done with its work, and the action invoker will call the ExecuteResult
method on that ViewResult instance, which does the rest. Here’s what the code might look like:

public ActionResult ListProducts()
{

//Pseudo code
IList<Product> products = SomeRepository.GetProducts();
ViewData.Model = products;
return new ViewResult {ViewData = this.ViewData };
}

In practice, you’ll probably never see code that instantiates an ActionResult instance directly like
that. Instead, you would use one of the helper methods of the Controller class, such as the View
method, as follows:

public ActionResult ListProducts()
{

//Pseudo code
IList<Product> products = SomeRepository.GetProducts();
return View(products);
}

The next chapter covers the ViewResult in more depth and tells how it relates to views.

Action Result Helper Methods

If you take a close look at the default controller actions in the default ASP.NET MVC project
template, you’ll notice that the action methods don’t directly instantiate instances of ViewResult.
For example, here’s the code for the About method:

public ActionResult About() {
ViewData["Title"] = "About Page";
return View();

}

504 ❘ CHAPTER 16 ADVANCED TOPICS

Notice that it returns the result of a call to the View method. The Controller class contains sev-
eral convenience methods for returning ActionResult instances. These methods are intended to
help make action method implementations a bit more readable and declarative. Instead of creating
new instances of action results, returning the result of one of these convenience methods is more
common.

These methods are generally named after the action result type that they return, with the Result
sufix omitted. Hence the View method returns an instance of ViewResult. Likewise, the Json
method returns an instance of JsonResult. The one exception in this case is the RedirectToAction
method, which returns an instance of RedirectToRoute.

The Redirect, RedirectToAction, and RedirectToRoute methods all send an HTTP 302 sta-
tus code, indicating a temporary redirection. In cases where content has moved permanently, you
want to tell clients that you are using an HTTP 301 status code. One of the primary beneits of
doing this is search engine optimization. When a search engine encounters an HTTP 301 code, it
will update the URLs displayed in search results; updating expired links can often have an impact
on search engine ranking as well. For this reason, each method that returns a RedirectResult
has a counterpart method that returns an HTTP 301 status code. These counterpart methods are
RedirectPermanent, RedirectToActionPermanent, and RedirectToRoutePermanent. Note that
browsers and other clients will cache HTTP 301 responses, so you should not use them unless you
are certain the redirect is permanent.

Table 16-4 lists the existing methods and which types they return.

TABLE 16-4: Controller Convenience Methods That Return ActionResult Instances

METHOD DESCRIPTION

Redirect Returns a RedirectResult, which redirects the user to the
RedirectPermanent appropriate URL.

RedirectToAction The same as Redirect but returns a RedirectResult with
RedirectToActionPermanent the Permanent property set to true, thus returning an HTTP
301 status code.
RedirectToRoute
RedirectToRoutePermanent Returns a RedirectToRouteResult, which redirects the user
to an action using the supplied route values.

The same as RedirectToAction but returns a
RedirectResult with the Permanent property set to true,
thus returning an HTTP 301 status code.

Returns a RedirectToRouteResult, which redirects the user
to the URL that matches the specified route values.

The same as RedirectToRoute but returns a
RedirectResult with the Permanent property set to true,
thus returning an HTTP 301 status code.

Advanced Controllers ❘ 505

METHOD DESCRIPTION

View Returns a ViewResult, which renders the view to the
PartialView response.
Content
File Returns a PartialViewResult, which renders a partial view to
Json the response.
JavaScript
Returns a ContentResult, which writes the specified content
(string) to the response.

Returns a class that derives from FileResult, which writes
binary content to the response.

Returns a JsonResult containing the output from serializing
an object to JSON.

Returns a JavaScriptResult containing JavaScript code that
is immediately executed when returned to the client.

Action Result Types

ASP.NET MVC includes several ActionResult types for performing common tasks, as listed in
Table 16-5. Each type is discussed in more detail in the sections that follow.

TABLE 16-5: Descriptions of ActionResult Types

ACTIONRESULT T YPE DESCRIPTION

ContentResult Writes the specified content directly to the response as text.
EmptyResult
FileContentResult Represents a null or empty response. It doesn’t do anything.
FilePathResult
Derives from FileResult and writes a byte array to the response.
FileResult
Derives from FileResult and writes a file to the response based
FileStreamResult on a file path.
HttpNotFound
Serves as the base class for a set of results that writes a binary
HttpStatusCodeResult response to the stream. Useful for returning files to the user.

Derives from FileResult and writes a stream to the response.

Derives from HttpStatusCodeResult. Returns an HTTP 404
response code to the client, indicating that the requested resource
is not found.

Returns a user-specified HTTP code.

continues

506 ❘ CHAPTER 16 ADVANCED TOPICS

TABLE 16-5 (continued)

ACTIONRESULT T YPE DESCRIPTION

HttpUnauthorizedResult Derives from HttpStatusCodeResult. Returns an HTTP 401
response code to the client, indicating that the requestor does not
JavaScriptResult have authorization to the resource at the requested URL.
JsonResult
PartialViewResult Used to execute JavaScript code immediately on the client sent
RedirectResult from the server.

RedirectToRouteResult Serializes the objects it is given into JSON and writes the JSON to
ViewResult the response, typically in response to an Ajax request.

This is similar to ViewResult, except it renders a partial view to the
response, typically in response to an Ajax request.

Redirects the requestor to another URL by returning either a
temporary redirect code 302 or permanent redirect code 301,
depending upon a Boolean Permanent flag.

Similar to RedirectResult, but redirects the user to a URL speci-
fied via Routing parameters.

Calls into a view engine to render a view to the response.

ContentResult

The ContentResult writes its speciied content (via the Content property) to the response. This
class also allows for specifying the content encoding (via the ContentEncoding property) and the
content type (via the ContentType property).

If the encoding is not speciied, the content encoding for the current HttpResponse instance is used.
The default encoding for HttpResponse is speciied in the globalization element of web.config.

Likewise, if the content type is not speciied, the content type set on the current HttpResponse
instance is used. The default content type for HttpResponse is text/html.

EmptyResult

As the name implies, the EmptyResult is used to indicate that the framework should do nothing.
This follows a common design pattern known as the Null Object pattern, which replaces null refer-
ences with an instance. In this instance, the ExecuteResult method has an empty implementation.
This design pattern was introduced in Martin Fowler’s book Refactoring: Improving the Design of
Existing Code (Addison-Wesley Professional, 1999). You can learn more at http://martinfowler.
com/bliki/refactoring.html.

FileResult

The FileResult is very similar to the ContentResult except that it is used to write binary content
(for example, a Microsoft Word document on disk or the data from a blob column in SQL Server) to
the response. Setting the FileDownloadName property on the result will set the appropriate value for
the Content-Disposition header, causing a ile download dialog to appear for the user.

Advanced Controllers ❘ 507

Note that FileResult is an abstract base class for three different ile result types:

➤ FilePathResult
➤ FileContentResult
➤ FileStreamResult

Usage typically follows the factory pattern in which the speciic type returned depends on which
overload of the File method (discussed later) is called.

HttpStatusCodeResult

The HttpStatusCodeResult provides a way to return an action result with a speciic HTTP
response status code and description. For example, to notify the requestor that a resource is
permanently unavailable, you could return a 410 (Gone) HTTP status code. Suppose you had
made the irm decision that your store would stop carrying disco albums. You could update your
StoreController Browse action to return a 410 if a user searched for disco:

public ActionResult Browse(string genre)
{

if(genre.Equals("disco",StringComparison.InvariantCultureIgnoreCase))
return new HttpStatusCodeResult(410);

var genreModel = new Genre { Name = genre };
return View(genreModel);
}

Note that there are ive speciic ActionResults based on common HTTP status codes, which were
previously described in Table 16-5:

➤ HttpNotFoundResult
➤ HttpStatusCodeResult
➤ HttpUnauthorizedResult
➤ RedirectResult
➤ RedirectToRouteResult

Both RedirectResult and RedirectToRouteResult (described later in this section) are based on
the common HTTP 301 and HTTP 302 response codes.

JavaScriptResult

The JavaScriptResult is used to execute JavaScript code on the client sent from the server. For
example, when using the built-in Ajax helpers to make a request to an action method, the method
could return a bit of JavaScript that is immediately executed when it gets to the client:

public ActionResult DoSomething() {
script s = "$('#some-div').html('Updated!');";

return JavaScript(s);
}

508 ❘ CHAPTER 16 ADVANCED TOPICS

This would be called by the following code:

<%: Ajax.ActionLink("click", "DoSomething", new AjaxOptions()) %>
<div id="some-div"></div>

This assumes that you’ve referenced the Ajax libraries and jQuery.

JsonResult

The JsonResult uses the JavaScriptSerializer class to serialize its contents (speciied via the
Data property) to the JSON (JavaScript Object Notation) format. This is useful for Ajax scenarios
that have a need for an action method to return data in a format easily consumable by JavaScript.

As for ContentResult, the content encoding and content type for the JsonResult can both be set
via properties. The only difference is that the default ContentType is application/json and not
text/html for this result.

Note that the JsonResult serializes the entire object graph. Thus, if you give it a ProductCategory
object, which has a collection of 20 Product instances, every Product instance will also be serial-
ized and included in the JSON sent to the response. Now imagine if each Product had an Orders
collection containing 20 Order instances. As you can imagine, the JSON response can grow huge
quickly.

There is currently no way to limit how much to serialize into the JSON, which can be problematic
with objects that contain a lot of properties and collections, such as those typically generated by
LINQ to SQL. The recommended approach is to create a type that contains the speciic information
you want included in the JsonResult. This is one situation in which an anonymous type comes in
handy.

For example, in the preceding scenario, instead of serializing an instance of ProductCategory,
you can use an anonymous object initializer to pass in just the data you need, as the following code
sample demonstrates:

public ActionResult PartialJson()
{

var category = new ProductCategory { Name="Partial"};
var result = new {

Name = category.Name,
ProductCount = category.Products.Count
};
return Json(result);
}

In this example, all you needed was the category name and the product count for the category.
Rather than serializing the entire object graph, you pulled the information you needed from the
actual object and stored that information in an anonymous type instance named result. You
then sent that instance to the response, rather than the entire object graph. Another beneit of this

Advanced Controllers ❘ 509

approach is that you won’t inadvertently serialize data you don’t want the client to see, such as any
internal product codes, stock quantity, supplier information, and so forth.

RedirectResult

The RedirectResult performs an HTTP redirect to the speciied URL (set via the Url property).
Internally, this result calls the HttpResponse.Redirect method, which sets the HTTP status code
to HTTP/1.1 302 Object Moved, causing the browser to immediately issue a new request for the
speciied URL.

Technically, you could just make a call to Response.Redirect directly within your action method,
but using the RedirectResult defers this action until after your action method inishes its work.
This is useful for unit testing your action method and helps keep underlying framework details out-
side of your action method.

RedirectToRouteResult

RedirectToRouteResult performs an HTTP redirect in the same manner as the RedirectResult,
but instead of specifying a URL directly, this result uses the Routing API to determine the
redirect URL.

Note that there are two convenience methods (deined in Table 16-4) that return a result of this
type: RedirectToRoute and RedirectToAction.

As discussed earlier, three additional methods return an HTTP 301 (Moved Permanently) status
code: RedirectPermanent, RedirectToActionPermanent, and RedirectToRoutePermanent.

ViewResult

The ViewResult is the most widely used action result type. It calls the FindView method of an
instance of IViewEngine, returning an instance of IView. The ViewResult then calls the Render
method on the IView instance, which renders the output to the response. In general, this inserts the
speciied view data (the data that the action method has prepared to be displayed in the view) into a
view template that formats the data for displaying.

PartialViewResult

PartialViewResult works in exactly the same way that ViewResult does, except that it calls
the FindPartialView method to locate a view rather than FindView. It’s used to render partial
views and is useful in partial update scenarios when using Ajax to update a portion of the page
with new HTML.

Implicit Action Results

One constant goal with ASP.NET MVC, and software development in general, is to make the inten-
tions of the code as clear as possible. There are times when you have a very simple action method
intended only to return a single piece of data. In this case, having your action method signature
relect the information that it returns is helpful.

510 ❘ CHAPTER 16 ADVANCED TOPICS

To highlight this point, consider a Distance method that calculates the distance between two
points. This action could write directly to the response — as shown in the irst controller actions in
Chapter 2, in the section titled “Writing Your First (Outrageously Simple) Controller.” However, an
action that returns a value can also be written as follows:

public double Distance(int x1, int y1, int x2, int y2)
{

double xSquared = Math.Pow(x2 - x1, 2);
double ySquared = Math.Pow(y2 - y1, 2);
return Math.Sqrt(xSquared + ySquared);
}

Notice that the return type is a double and not a type that derives from ActionResult. This is
perfectly acceptable. When ASP.NET MVC calls that method and sees that the return type is not an
ActionResult, it automatically creates a ContentResult containing the result of the action method
and uses that internally as the ActionResult.

One thing to keep in mind is that the ContentResult requires a string value, so the result of your
action method needs to be converted to a string irst. To do this, ASP.NET MVC calls the ToString
method on the result, using InvariantCulture, before passing it to the ContentResult. If you
need to have the result formatted according to a speciic culture, you should explicitly return a
ContentResult yourself.

In the end, the preceding method is roughly equivalent to the following method:

public ActionResult Distance(int x1, int y1, int x2, int y2)
{

double xSquared = Math.Pow(x2 - x1, 2);
double ySquared = Math.Pow(y2 - y1, 2);
double distance = Math.Sqrt(xSquared + ySquared);
return Content(Convert.ToString(distance, CultureInfo.InvariantCulture));
}

The advantages of the irst approach are that it makes your intentions clearer, and the method is
easier to unit test.

Table 16-6 highlights the various implicit conversions you can expect when writing action methods
that do not have a return type of ActionResult.

TABLE 16-6: Implicit Conversions with Action Methods

RETURN VALUE DESCRIPTION

Null The action invoker replaces null results with an instance of EmptyResult.
This follows the Null Object Pattern. As a result, implementers writing cus-
Void tom action filters don’t have to worry about null action results.

Other objects that The action invoker treats the action method as if it returned null, and thus
don’t derive from an EmptyResult is returned.
ActionResult
The action invoker calls ToString using InvariantCulture on the object
and wraps the resulting string in a ContentResult instance.

Advanced Controllers ❘ 511

NOTE The code to create a ContentResult instance is encapsulated in a virtual
method on the action invoker called CreateActionResult. For those who want
to return a different implicit action result type, you can write a customer action
invoker that derives from ControllerActionInvoker and override that method.
One example might be to have return values from action methods automatically
be wrapped by a JsonResult.

Action Invoker

We’ve made several references in this chapter to the action invoker without giving any details
about it. Well, no more arm waving! This section covers the role of a critical element in the ASP.
NET MVC request processing chain: the thing that actually invokes the action you’re calling — the
action invoker. When we irst deined the controller earlier in this chapter, we looked at how
Routing maps a URL to an action method on a Controller class. Diving deeper into the details,
you learned that routes themselves do not map anything to controller actions; they merely parse the
incoming request and populate a RouteData instance stored in the current RequestContext.
It’s the ControllerActionInvoker, set via the ActionInvoker property on the Controller class,
that is responsible for invoking the action method on the controller based on the current request
context. The invoker performs the following tasks:

➤ It locates the action method to call.
➤ It gets values for the parameters of the action method by using the model binding system.
➤ It invokes the action method and all its ilters.
➤ It calls ExecuteResult on the ActionResult returned by the action method. For meth-

ods that do not return an ActionResult, the invoker creates an implicit action result as
described in the previous section and calls ExecuteResult on that.

In the next section, you’ll take a closer look at how the invoker locates an action method.

How an Action Is Mapped to a Method

The ControllerActionInvoker looks in the route values dictionary associated with the current
request context for a value corresponding to the action key. As an example, here is the URL pattern
for the default route:

{controller}/{action}/{id}

When a request comes in and matches that route, a dictionary of route values (accessible via the
RequestContext) is populated based on this route. For example, if a request comes in for

/home/list/123

512 ❘ CHAPTER 16 ADVANCED TOPICS

Routing adds the value list with a key of action to the route values dictionary.

At this point within the request, an action is just a string extracted from the URL; it is not a
method. The string represents the name of the action that should handle this request. Though it
may commonly be represented by a method, the action really is an abstraction. There might be
more than one method that can respond to the action name. Or it might not even be a method but a
worklow or some other mechanism that can handle the action.

The point is that, while in the general case an action typically maps to a method, it doesn’t have
to. You’ll see an example of this later in the chapter, where we discuss asynchronous actions where
there are two methods per action.

Action Method Selection

After the invoker has determined the action’s name, it attempts to identify a method that can
respond to that action. By default, the invoker uses relection to ind a public method on a class that
derives from a Controller that has the same name (case insensitive) as the current action. Such a
method must meet the following criteria:

➤ An action method must not have the NonActionAttribute deined.

➤ Special methods such as constructors, property accessors, and event accessors cannot be
action methods.

➤ Methods originally deined on Object (such as ToString) or on Controller (such as
Dispose or View) cannot be action methods.

Like many features of ASP.NET MVC, you can tweak this default behavior to suit any special needs
your applications might have.

ActionNameAttribute

Applying the ActionNameAttribute attribute to a method allows you to specify the action that the
method handles. For example, suppose that you want to have an action named View. Unfortunately,
this would conlict with the built-in View method of Controller that’s used to return a
ViewResult. An easy way to work around this issue is to do the following:

[ActionName("View")]
public ActionResult ViewSomething(string id)
{

return View();
}

The ActionNameAttribute redeines the name of this action as View. Thus, this method is invoked
in response to requests for /home/view, but not for /home/viewsomething. In the latter case, as far
as the action invoker is concerned, an action method named ViewSomething does not exist.

One consequence of this is that if you’re using our conventional approach to locate the view that
corresponds to this action, the view should be named after the action, not after the method. In the
preceding example (assuming that this is a method of HomeController), you would look for the
view in ~/Views/Home/View.cshtml by default.

Advanced Controllers ❘ 513

This attribute is not required for an action method. There is an implicit rule that the name of the
action method serves as the action name if this attribute is not applied.

ActionSelectorAttribute

You’re not done matching the action to a method yet. After you’ve identiied all methods of the
Controller class that match the current action name, you need to whittle down the list further by
looking at all instances of the ActionSelectorAttribute applied to the methods in the list.

This attribute is an abstract base class for attributes that provide ine-grained control over which
requests an action method can respond to. The API for this method consists of a single method:

public abstract class ActionSelectorAttribute : Attribute
{

public abstract bool IsValidForRequest(ControllerContext controllerContext,
MethodInfo methodInfo);

}

At this point, the invoker looks for any methods in the list that contain attributes that derive from
this attribute and calls the IsValidForRequest method on each attribute. If any attribute returns
false, the method that the attribute is applied to is removed from the list of potential action
methods for the current request.

At the end, you should be left with one method in the list, which the invoker then invokes. If more
than one method can handle the current request, the invoker throws an exception indicating that
there is an ambiguity in the method to call. If no method can handle the request, the invoker calls
HandleUnknownAction on the controller.

The ASP.NET MVC framework includes two implementations of this base attribute: the
AcceptVerbsAttribute and the NonActionAttribute.

AcceptVerbsAttribute

AcceptVerbsAttribute is a concrete implementation of ActionSelectorAttribute that uses the
current HTTP request’s HTTP method (verb) to determine whether or not a method is the action
that should handle the current request. This allows you to have method overloads, both of which are
actions but respond to different HTTP verbs.

MVC includes a more terse syntax for HTTP method restriction with the [HttpGet], [HttpPost],
[HttpDelete], [HttpPut], and [HttpHead] attributes. These are simple aliases for the previous
[AcceptVerbs(HttpVerbs.Get)], [AcceptVerbs(HttpVerbs.Post)], [AcceptVerbs(HttpVerbs.
Delete)], [AcceptVerbs(HttpVerbs.Put)], and [AcceptVerbs(HttpVerbs.Head)] attributes,
but are easier to both type and read.

For example, you might want two versions of the Edit method: one that renders the edit form and
the other that handles the request when that form is posted:

[HttpGet]
public ActionResult Edit(string id)
{

return View();
}

514 ❘ CHAPTER 16 ADVANCED TOPICS

[HttpPost]
public ActionResult Edit(string id, FormCollection form)
{

//Save the item and redirect…
}

When a POST request for /home/edit is received, the action invoker creates a list of all methods
of the controller that match the edit action name. In this case, you would end up with a list of two
methods. Afterward, the invoker looks at all the ActionSelectorAttribute instances applied to
each method and calls the IsValidForRequest method on each. If each attribute returns true, the
method is considered valid for the current action.

For example, in this case, when you ask the irst method whether it can handle a POST request, it
will respond with false because it handles only GET requests. The second method responds with
true because it can handle the POST request, and it is the one selected to handle the action.

If no method is found that meets these criteria, the invoker will call the HandleUnknownAction
method on the controller, supplying the name of the missing action. If more than one action method
meeting these criteria is found, an InvalidOperationException is thrown.

Simulating RESTful Verbs

Most browsers support only two HTTP verbs during normal web browsing: GET and POST.
However, the REST architectural style also makes use of a few additional standard verbs:
DELETE, HEAD, and PUT. ASP.NET MVC allows you to simulate these verbs via the Html.
HttpMethodOverride helper method, which takes a parameter to indicate one of the standard
HTTP verbs (DELETE, GET, HEAD, POST, and PUT). Internally, this works by sending the verb
in an X-HTTP-Method-Override form ield.

The behavior of HttpMethodOverride is complemented by the [AcceptVerbs] attribute as well as
the new shorter verb attributes:

➤ HttpPostAttribute

➤ HttpPutAttribute

➤ HttpGetAttribute

➤ HttpDeleteAttribute

➤ HttpHeadAttribute

Though the HTTP method override can be used only when the real request is a POST request, the
override value can also be speciied in an HTTP header or in a query string value as a name/value pair.

MORE ON OVERRIDING HTTP VERBS

Overriding HTTP verbs via X-HTTP-Method-Override is not an oficial standard,
but it has become a common convention. It was irst introduced by Google as part
of the Google Data Protocol in 2006 (http://code.google.com/apis/gdata/
docs/2.0/basics.html), and has since been implemented in a variety of RESTful
web APIs and web frameworks. Ruby on Rails follows the same pattern, but uses
a _method form ield instead of X-HTTP-Method-Override.


Click to View FlipBook Version