Advanced Topics - Professional ASP.NET MVC 5 (2014)

Professional ASP.NET MVC 5 (2014)

Chapter 16
Advanced Topics

What's In This Chapter?

· Using mobile support

· Understanding advanced Razor features

· Working with view engines

· Understanding and customizing scaffolding

· Working with tricky routing scenarios

· Customizing templates

· Using controllers in advanced scenarios

In previous chapters in this book, we postponed discussing some of our favorite advanced topics because they could distract you from the concepts. Now that you've made it this far, you have the fundamentals down and we can share more of that advanced content with you. Let's go!

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

All code for this chapter is provided via NuGet as described in the book introduction. NuGet code samples are clearly indicated with notes at the end of each application section. The NuGet packages are also available at http://www.wrox.com/go/proaspnetmvc5.

MOBILE SUPPORT

Using mobile devices for viewing websites is becoming increasingly common. Some estimates show mobile devices account for 30 percent of website traffic, and it's on the rise. Thinking about your site's appearance and usability on mobile devices is important.

A variety of approaches exist for enhancing the mobile experience of your web application. In some cases, you just want to make some minor style changes on smaller display resolutions. In others you might want to completely change the visual appearance or content of some views. In the most extreme case (before moving from mobile web application to native mobile application), you might want to create a web application that is specifically targeted at mobile users. MVC provides two main options to target each of these scenarios:

· Adaptive rendering: The default; Bootstrap-based application templates use CSS media queries to gracefully scale down to smaller mobile form factors.

· Display modes: MVC supports a convention-based approach to allow selecting different views based on the browser's making the request. Unlike adaptive rendering, this allows you to change the markup that's sent to mobile browsers.

Mobile Emulators

The screenshots in this section use Windows Phone Emulator, which is included in the Windows Phone SDK. The Windows Phone SDK is included with Visual Studio 2013, and is available separately from https://dev.windowsphone.com/en-us/downloadsdk.

I encourage you to try some other mobile emulators, such as the Opera Mobile Emulator (http://www.opera.com/developer/tools/mobile/) or the Electric Plum Simulator for iPhone and iPad browsers (http://www.electricplum.com).

Adaptive Rendering

The first step in improving your site's mobile experience is taking a look at your site in a mobile browser. As an extreme example, Figure 16.1 shows how the MVC 3 default template homepage looks when viewed on a mobile device.

image

Figure 16.1

This experience reveals a number of problems:

· A lot of the text isn't even readable at the default zoom level.

· The navigation links in the header are unusable.

· Zooming in doesn't really help because the content doesn't reflow, so you're stuck looking at a tiny portion of the page.

And that's just a quick list based on a very simple page.

Fortunately, the MVC project templates have improved a lot since MVC 4 added some custom HTML and CSS, which leveraged some of the browser features you'll be looking at in just a minute. MVC 5 takes this quite a bit further by basing the project templates on the Bootstrap framework. Bootstrap places a lot of emphasis on working well on mobile devices, to the point that Bootstrap 3 (the version shipping with MVC 5) calls itself a framework for “mobile first projects on the Web.” Although, you can certainly build great web applications targeting widescreen, desktop displays (as you've seen in this book), mobile-friendly layouts are more than just supported in Bootstrap 3—they're a first-class concern.

Therefore, that an MVC 5 application fares a lot better in a mobile browser without additional work on your part, is no surprise, as shown in Figure 16.2.

image

Figure 16.2

What's immediately obvious is that the page in Figure 16.2 is intelligently scaled to the screen size of the mobile device. Rather than just scaling the page down (shrinking text and all), the page is restyled so that it's usable in the device's dimensions.

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 five separate text links to a single drop-down menu, as shown inFigure 16.3.

image

Figure 16.3

Scrolling down further, you can see the other simplifications to the mobile view to tighten it up and maximize the screen real estate. Although the changes are subtle, they make a difference. For example, form fields 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 supported 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 specifically designed for small form factors, based on browser sniffing 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-specific 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 defined. Media queries extend the functionality of media types by allowing more precise labeling of style sheets.

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,’ andcolor.’ By using media queries,presentations can be tailored to a specific 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 general rules at the top of your CSS file and override them with rules specific 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 first” framework, it actually flips that around using min-widthqueries. 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 intermediate 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.

image

Figure 16.4

For comparison, Figure 16.5 shows that same browser window resized to just over 768px.

image

Figure 16.5

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 responsive web design. Responsive web design also addresses other concerns such as fluid 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.

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.

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 first 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 configure.

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.

image

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.

image

Figure 16.7

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 templates, 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.

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 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 configure. 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 difficult to navigate the site and read the content. We were able to dramatically improve the site experience through adaptive rendering, and have since seen significantly 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 reference that parameter as many times as it needs to.

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

<div>

@strongTemplate("This is bolded.")

</div>

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 compiled at run time into classes and then executed. The compilation happens the first time the view is requested, which incurs at a slight one-time performance cost. The benefit 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 defined within the Web.config file in the Views directory. The following section ofWeb.config contains the Razor configuration:

<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 specifies the base page type for all Razor views in your application. You can change that value by replacing 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 specified, 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 define an optional layout section with default content without requiring the this keyword:

<footer>

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

</footer>

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 file 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 file 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-specific language) as input.

To better understand what a view engine is, let's review the ASP.NET MVC life cycle (very simplified in Figure 16.8).

image

Figure 16.8

A lot more subsystems are involved; this figure 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 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 configured in Global.asax.cs. By default, there is no need to register other view engines if you stick with just using RazorViewEngine (and the WebFormViewEngineis also registered 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 first 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 project, simply run the NuGet command Install-Package Spark.Web.Mvc. This adds and configures 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 defined 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 displayed. This declarative approach to controlling markup output is a hallmark of Spark.

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 registered view engines and calls FindView on each one, passing in the specified 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 returns null.

SearchedLocations

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 file paths for view engines that use a template file, but they could be something else entirely, such as database locations for view engines that store views in a database. These location 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 finding a partial view. It is quite common for view engines to treat views and partial views differently. 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.

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 consume 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, Request, Response.

Controller

An instance of ControllerBase, which provides access to the controller, making the call to the view engine.

RouteData

An instance of RouteData, which provides access to the route values for the current request.

ViewData

An instance of ViewDataDictionary containing the data passed from the controller to the view.

TempData

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

View

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

ClientValidationEnabled

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

FormContext

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

FormIdGenerator

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

IsChildAction

Boolean value indicating whether the action is being displayed as a result of a call to Html.Action or Html.RenderAction.

ParentActionViewContext

When IsChildAction is true, contains the ViewContext of this view's parent view.

Writer

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.

UnobtrusiveJavaScriptEnabled

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 first 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

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.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 innovative features, including support for the Jade templating language first popularized on Node.js.

NHaml

NHaml (hosted on GitHub at https://github.com/NHaml/NHaml), created 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 the structure of XHTML with a minimum of characters.

Brail

Brail (part of the MvcContrib project, http://mvccontrib.org) is interesting for its use of the Boo Language. Boo is an object-oriented statically typed language for the CLR with a Python language style to it, such as significant white space.

StringTemplate

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

Nustache (https://github.com/jdiamond/Nustache) is a .NET implementation 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 control flow statements. The Nustache project includes an MVC view engine.

Parrot

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 (JSVE)

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.

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 format. Should you write a custom view engine or a newMyCustomXmlFormatActionResult?

The general rule for choosing between one and the other is whether it makes sense to have some sort of template file 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 files as the view templates.

ADVANCED SCAFFOLDING

Chapter 4 overviewed the MVC 5 use of scaffolded views, which make it easy to create the controller 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 first 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.

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 find out more information on the T4 system and syntax at http://msdn.microsoft.com/en-us/library/bb126445.aspx.

Note

Visual Studio just displays T4 templates (files 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 modified 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 files 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 SideWaffle. The SideWaffle extension makes it really easy to add snippets, Project templates, and Item templates to your project. The SideWaffle website (http://sidewaffle.com) has more information, a list of available templates, and a link to download the extension. After installing the SideWaffle extension, you can add the CodeTemplates directory to any project using Add / New Item dialog, selecting the Web / SideWaffle group, and selecting the ASP.NET Scaffolding T4 files template as shown in Figure 16.9.

image

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.

image

Figure 16.10

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.

image

Figure 16.11

Customizing the scaffold templates is an easy, low-risk way to make your whole team more productive. If you find 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 customizing 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 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 walkthrough 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 SideWaffle extension for Visual Studio 2013.

2. Create a new project in Visual Studio.

3. In the New Project dialog, select the Templates Extensibility SideWaffle 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 files and folders, generating code using T4 templates, and adding NuGet packages.

7. Test and build the solution. This creates a VSIX file.

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

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 redirect routes can help you support that.

One of the benefits 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 find 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 defined.

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 misconfigured route could seriously tank your application.

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

The RouteMagic project just mentioned includes support for routes that can be modified 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).

image

Figure 16.12

Next, use Visual Studio's Properties dialog to mark the file's Build Action as “Content” so that it's not compiled into the application, as shown in Figure 16.13.

image

Figure 16.13

Setting the Build Action to “Content” intentionally excludes the Routes.cs file 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.)

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 defined in the RouteMagic assembly. This interface defines 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");

}

}

}

With this in place, you can now change routes within the Routes.cs file 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 file. 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 notification of when the Routes.cs file changes, so we'll know it needs to be rebuilt. The ASP.NET Cache allows us to set a cache dependency on a file and a method to call when the file changes (invalidating the cache).

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 file change notifications 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.

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 render 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 constructing HTML. Each helper will select a template based on information about the model — both the model type and model metadata. For example, imagine a boolproperty 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 based on the type name of the value it is rendering. When you askEditorFor 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 official source for current default templates, but you can get a good idea of what they're doing by reading 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" })

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 specified). 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.

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 defined in System.Web.Mvc.Html.DefaultEditorTemplates—types such as Int32 and DateTime?

Before checking for a template matching the type name, the framework first 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. TheDataType attribute can also influence 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 find 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 reflection 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">

@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 (usingNullDisplayText 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 define your own templates and override the defaults.

Custom Templates

Custom templates will live in a DisplayTemplates or EditorTemplates folder. The MVC framework follows a familiar set of rules when it resolves the path to a template. First, it looks underneath the folder associated with a specific 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 associated with every view engine configured into the application (so by default, the framework looks for templates with .aspx, .ascx, and .cshtmlextensions).

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).

image

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\EditorTemplatesfolder 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 editor for a DateTime property. The framework will render a DateTimeproperty 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"

})

You could place the preceding code inside a file named DateTime.cshtml, and place the file 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 handful of special cases. In that case, you could name the template file 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 file.

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 find 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 defined 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 and flexibility. When building software with these focuses, leveraging abstraction as much as possible 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 suffix Controller. The naming convention is actually quite important — and you'll find 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 define configuration 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 identifies 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 facility for Routing to find 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.

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 interface 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 sending data to a view, discussed in Chapter 3). The Execute method ofControllerBase is responsible for creating the ControllerContext, which provides the MVC-specific 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 benefiting from the action filter infrastructure in ASP.NET MVC (action filters allow for filtering and working with request/response data, as discussed 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 overall 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 IController interface. The Controller class is intended to serve as the base class for all controllers 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.

image

Figure 16.15

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 factor your controller into action methods, each of which responds to a specific user input.

Product Team Aside

Upon reading that every public method of your Controller class is publicly callable 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 concerning 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 first 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 define 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 define 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);

}

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 application 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 filtering 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 defined 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 concerns 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();

}

Notice that it returns the result of a call to the View method. The Controller class contains several 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 suffix 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 theRedirectToAction method, which returns an instance of RedirectToRoute.

The Redirect, RedirectToAction, and RedirectToRoute methods all send an HTTP 302 status 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 benefits 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 appropriate URL.

RedirectPermanent

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

RedirectToAction

Returns a RedirectToRouteResult, which redirects the user to an action using the supplied route values.

RedirectToActionPermanent

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

RedirectToRoute

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

RedirectToRoutePermanent

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

View

Returns a ViewResult, which renders the view to the response.

PartialView

Returns a PartialViewResult, which renders a partial view to the response.

Content

Returns a ContentResult, which writes the specified content (string) to the response.

File

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

Json

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

JavaScript

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 Type

Description

ContentResult

Writes the specified content directly to the response as text.

EmptyResult

Represents a null or empty response. It doesn't do anything.

FileContentResult

Derives from FileResult and writes a byte array to the response.

FilePathResult

Derives from FileResult and writes a file to the response based on a file path.

FileResult

Serves as the base class for a set of results that writes a binary response to the stream. Useful for returning files to the user.

FileStreamResult

Derives from FileResult and writes a stream to the response.

HttpNotFound

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

HttpStatusCodeResult

Returns a user-specified HTTP code.

HttpUnauthorizedResult

Derives from HttpStatusCodeResult. Returns an HTTP 401 response code to the client, indicating that the requestor does not have authorization to the resource at the requested URL.

JavaScriptResult

Used to execute JavaScript code immediately on the client sent from the server.

JsonResult

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

PartialViewResult

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

RedirectResult

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.

RedirectToRouteResult

Similar to RedirectResult, but redirects the user to a URL specified via Routing parameters.

ViewResult

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

ContentResult

The ContentResult writes its specified 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 specified, the content encoding for the current HttpResponse instance is used. The default encoding for HttpResponse is specified in the globalization element of web.config.

Likewise, if the content type is not specified, 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 references 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 file download dialog to appear for the user.

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

· FilePathResult

· FileContentResult

· FileStreamResult

Usage typically follows the factory pattern in which the specific 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 specific 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 firm 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 five specific 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);

}

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 (specified 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 serialized and included in the JSON sent to the response. Now imagine if eachProduct 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 specific 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 benefit of this 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 specified 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 specified 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 finishes its work. This is useful for unit testing your action method and helps keep underlying framework details outside 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 (defined 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 specified 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 intentions 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 reflect the information that it returns is helpful.

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 first 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 first. To do this, ASP.NET MVC calls the ToString method on the result, using InvariantCulture, before passing it to theContentResult. If you need to have the result formatted according to a specific 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 first 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 custom action filters don't have to worry about null action results.

Void

The action invoker treats the action method as if it returned null, and thus an EmptyResult is returned.

Other objects that don't derive from ActionResult

The action invoker calls ToString using InvariantCulture on the object and wraps the resulting string in a ContentResult instance.

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 first defined 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 filters.

· It calls ExecuteResult on the ActionResult returned by the action method. For methods 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

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 workflow 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 reflection to find 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 defined.

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

· Methods originally defined 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 conflict 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 redefines 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 ViewSomethingdoes 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.

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 identified 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 fine-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();

}

[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 ActionSelectorAttributeinstances 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 first 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 field.

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 specified 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 official standard, but it has become a common convention. It was first 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 field instead of X-HTTP-Method-Override.

MVC allows the override for POST requests only. The framework will look for the overridden verb first from the HTTP headers, then from POST values, and, finally, from query string values.

Invoking Actions

Next the invoker uses the model binder (discussed in depth in Chapter 4, in the “Model Binding” section) to map values for each parameter of the action method, and is then finally ready to invoke the action method itself. At this point, the invoker builds up a list of filters associated with the current action method and invokes the filters along with the action method, in the correct order. For more detailed coverage of this, see the “Filters” section of Chapter 15.

Using Asynchronous Controller Actions

ASP.NET MVC 2 and later include full support for an asynchronous request pipeline. The purpose of this asynchronous pipeline is to allow the web server to handle long-running requests — such as those that spend a large amount of time waiting for a network or database operation to complete — while still remaining responsive to other requests. In this regard, asynchronous code is about servicing requests more efficiently than it is about servicing an individual request more quickly.

Although earlier versions of MVC supported asynchronous actions, taking advantage of this capability prior to MVC 4 was difficult. MVC 4 and later leverage the following recent .NET Framework features to greatly simplify the process:

· .NET 4 introduced a new Task Parallel Library to simplify the development work to support parallelism and concurrency in .NET applications. The Task Parallel Library includes a new type, the Task, to represent an asynchronous operation. MVC 5 supports this by allowing you to return Task<ActionResult> from an action method.

· .NET 4.5 further simplifies asynchronous programming through two new keywords, async and await. The async modifier notifies the compiler that a method (including anonymous methods and lambda expressions) is asynchronous, containing one or more long-running operations. The await keyword is applied to tasks within an asynchronous method, indicating that the method should be suspended until the awaited task completes.

· The combination of .NET 4 Tasks and .NET 4.5 async and await support is referred to as the Task-based Asynchronous Pattern, or TAP. Writing asynchronous controller actions using TAP support in MVC 5 is significantly easier than the prior solution in MVC 2 and 3. In this section, you focus on using TAP with MVC 5 on .NET 4.5.

To understand the difference between asynchronous and synchronous ASP.NET code, one must first have a basic knowledge of how requests are processed by the web server. IIS maintains a collection of idle threads (the thread pool) that are used to service requests. When a request comes in, a thread from the pool is scheduled to process that request. While a thread is processing a request, it cannot be used to process any other requests until it has finished with the first. The ability of IIS to service multiple requests simultaneously is based on the assumption that there will be free threads in the pool to process incoming requests.

Now consider an action that makes a network call as part of its execution, and consider that the network call might take two seconds to complete. From the site visitor's point of view, the server takes about two seconds to respond to his or her request, if you take into account a little bit of overhead on the web server itself. In a synchronous world, the thread processing the request is blocked for the two seconds that the network call is taking place. That is, the thread cannot perform useful work for the current request because it's waiting for the network call to complete, but it also can't do any useful work for any other request because it's still scheduled to work on the first request. A thread in this condition is known as a blocked thread. Normally this isn't a problem because the thread pool is large enough to accommodate such scenarios. However, in large applications that process multiple simultaneous requests, this can lead to many threads being blocked waiting for data and not enough idle threads left in the thread pool available for dispatch for servicing new incoming requests. This condition is known as thread starvation, and it can severely affect the performance of a website (see Figure 16.16).

image

Figure 16.16

In an asynchronous pipeline, threads are not blocked waiting for data. When a long-running application such as a network call begins, the action is responsible for voluntarily relinquishing control of the thread for the duration of the operation. Essentially, the action tells the thread, “It'll be a while before I can continue, so don't bother waiting for me right now. I'll notify IIS when the data I need is available.” The thread is then returned to the thread pool so that it can handle another request, and the current request is essentially paused while waiting for data. Importantly, while a request is in this state, it is not assigned to any thread from the thread pool, so it is not blocking other requests from being processed. When the action's data becomes available, the network request completion event notifies IIS and a free thread from the thread pool is dispatched to continue processing the request. The thread that continues processing the request may or may not be the same thread that originated the request, but the pipeline takes care of this so that developers don't have to worry about it (see Figure 16.17).

image

Figure 16.17

It is important to note that in the previous example, the end user still sees a two-second delay between the time he sends the request and the time he receives a response from the server. This is what is meant by the earlier statement about asynchronous being primarily for efficiency rather than the response speed for an individual request. Even though it takes the same amount of time to respond to the user's request regardless of whether the operation is synchronous or asynchronous, in an asynchronous pipeline the server is not blocked from doing other useful work while waiting for the first request to complete.

Choosing Synchronous versus Asynchronous Pipelines

The following are some guidelines for deciding whether to use synchronous or asynchronous pipelines. Note that these are just guidelines and each application will have its own requirements.

Use synchronous pipelines when:

· The operations are simple or short-running.

· Simplicity and testability are important.

· The operations are CPU-bound rather than IO-bound.

Use asynchronous pipelines when:

· Testing shows that blocking operations are bottlenecking site performance.

· Parallelism is more important than simplicity of code.

· The operations are IO-bound rather than CPU-bound.

Because asynchronous pipelines have more infrastructure and overhead than synchronous pipelines, asynchronous code is somewhat more difficult to reason about than synchronous code. Testing such code would require mocking more of the infrastructure, and it would also require taking into account that the code can execute in many different orderings. Finally, converting a CPU-bound operation to an asynchronous operation is not really beneficial, because all that does is add overhead to an operation that probably wasn't blocked to begin with. In particular, this means that code that performs CPU-bound work within the ThreadPool.QueueUserWorkItem() method will not benefit from an asynchronous pipeline.

Writing Asynchronous Action Methods

Asynchronous actions using the new TAP model in MVC 5 are very similar to standard (synchronous) actions. Here are the requirements for converting an action to an asynchronous action:

· The action method must be marked as asynchronous using the async modifier.

· The action must return either Task or Task<ActionResult>.

· Any asynchronous operations within the method use the await keyword to suspend operation until the call has completed.

For example, consider a portal site that displays news for a given area. The news in this example is provided via a GetNews()method that involves a network call that could be long running. A typical synchronous action might look like this:

public class PortalController : Controller {

public ActionResult News(string city) {

NewsService newsService = new NewsService();

NewsModel news = newsService.GetNews(city);

return View(news);

}

}

Here is that same action converted to an asynchronous action:

public class PortalController : Controller {

public async Task<ActionResult> News(string city) {

NewsService newsService = new NewsService();

NewsModel news = await newsService.GetNews(city);

return View(news);

}

}

As described earlier, you only have to make three changes: add the async modifier to the action, return a Task<ActionResult>, and add an await before the call to the long-running service.

When Would You Just Return Task?

You might have wondered why MVC 5 supports returning Task as well as Task<ActionResult>. What's the point of an action that doesn't return anything?

It turns out that this is pretty useful in long-running service operations that don't need to return any output. For instance, you might have an action that performs a lengthy service operation, such as sending bulk e-mail or building a large report. In those cases, there's nothing to return and there's no caller listening. Returning Task is the same as returning void from a synchronous action; both are converted to an EmptyResult response, which means no response is sent.

Performing Multiple Parallel Operations

The preceding example won't perform any faster than a standard synchronous action; it just allows for more efficient use of server resources (as explained at the beginning of this section). One of the greatest benefits of asynchronous code can be seen when an action wants to perform several asynchronous operations at a time. For example, a typical portal site would show not only news, but also sports, weather, stocks, and other information:

public class PortalController : Controller {

public ActionResult Index(string city) {

NewsService newsService = new NewsService();

WeatherService weatherService = new WeatherService();

SportsService sportsService = new SportsService();

PortalViewModel model = new PortalViewModel {

News = newsService.GetNews(city),

Weather = weatherService.GetWeather(city),

Sports = sportsService.GetScores(city)

};

return View(model);

}

}

Note that the calls are performed sequentially, so the time required to respond to the user is equal to the sum of the times required to make each individual call. If the calls are 200, 300, and 400 milliseconds (ms), then the total action execution time is 900 ms (plus some insignificant overhead).

Similarly, an asynchronous version of that action would take the following form:

public class PortalController : Controller {

public async Task<ActionResult> Index(string city) {

NewsService newsService = new NewsService();

WeatherService weatherService = new WeatherService();

SportsService sportsService = new SportsService();

var newsTask = newsService.GetNewsAsync(city);

var weatherTask = weatherService.GetWeatherAsync(city);

var sportsTask = sportsService.GetScoresAsync(city);

await Task.WhenAll(newsTask, weatherTask, sportsTask);

PortalViewModel model = new PortalViewModel {

News = newsTask.Result,

Weather = weatherTask.Result,

Sports = sportsTask.Result

};

return View(model);

}

}

Note that the operations are all kicked off in parallel, so the time required to respond to the user is equal to the longest individual call time. If the calls are 200, 300, and 400 ms, then the total action execution time is 400 ms (plus some insignificant overhead).

Parallel Task Calls using Task.WhenAll

Note that we used the Task.WhenAll() method to execute multiple tasks in parallel. You might think that just adding the await keyword to each of our service calls would parallelize them, but that's not the case. Although await does release the thread until a long-running call completes, the second awaited call won't start until the first completes. Task.WhenAll executes all tasks in parallel and returns when all tasks are complete.

In both of the preceding examples, the URL to access the action is /Portal/Index?city=Seattle (or /Portal?city=Seattle, using the default route), and the view page name is Index.cshtml (because the action name is Index).

This is a classic example where async is used not only for efficiency, but for performance as well (from the end user's perspective).

SUMMARY

Throughout this book, we've been careful not to flood you with information that, while interesting, would get in the way of learning the important concepts. We've had to avoid talking about interesting interactions between components we hadn't discussed yet, and we've avoided burrowing deep into implementation details that thrill us but might baffle learners.

In this chapter, though, we've been able to talk to you like the informed developer that you are, sharing some of our favorite tidbits about the inner workings of ASP.NET MVC, as well as advanced techniques to get the most from the framework. We hope you've enjoyed it as much as we have!