Microsoft ASP.NET MVC is a web application development framework built on top of Microsoft’s popular and mature .NET Framework. The ASP.NET MVC Framework leans heavily on proven developmental patterns and practices that place an emphasis on a loosely coupled application architecture and highly maintainable code.
In this chapter we’ll take a look at the fundamentals of what makes ASP.NET MVC tick—from its proud lineage and the architectural concepts on which it is built, to the use of Microsoft Visual Studio 2011 to create a fully functioning ASP.NET MVC web application. Then we’ll dive into the ASP.NET MVC web application project and see just what ASP.NET MVC gives you right from the start, including a working web page and built-in forms authentication to allow users to register and log in to your site.
By the end of the chapter, you’ll have not only a working ASP.NET MVC web application, but also enough understanding of the fundamentals of ASP.NET MVC to begin building applications with it immediately. The rest of this book simply builds on these fundamentals, showing you how to make the most of the ASP.NET MVC Framework in any web application.
Understanding the past can be a big help in appreciating the present; so, before we get into what ASP.NET MVC is and how it works, let’s take a minute to see just where it came from.
Long ago, Microsoft saw the need for a Windows-based web development platform, and the company worked hard to produce a solution. Over the past two decades, Microsoft has given the development community several web development platforms.
Microsoft’s first answer to web development was Active Server Pages (ASP), a scripting language in which code and markup are authored together in a single file, with each physical file corresponding to a page on the website. ASP’s server-side scripting approach became widely popular and many websites grew out of it. Some of these sites continue to serve visitors today. After a while, though, developers wanted more. They asked for features such as improved code reuse, better separation of concerns, and easier application of object-oriented programming principles. In 2002, Microsoft offered ASP.NET as a solution to these concerns.
Like ASP, ASP.NET websites rely on a page-based approach where each page on the website is represented in the form of a physical file (called a Web Form) and is accessible using that file’s name. Unlike a page using ASP, a Web Forms page provides some separation of code and markup by splitting the web content into two different files: one for the markup and one for the code. ASP.NET and the Web Forms approach served developers’ needs for many years, and this continues to be the web development framework of choice for many .NET developers. Some .NET developers, however, consider the Web Forms approach too much of an abstraction from the underlying HTML, JavaScript, and CSS. Some developers just can’t be pleased! Or can they?
Microsoft was quick to spot the growing need in the ASP.NET developer community for something different than the page-based Web Forms approach, and the company released the first version of ASP.NET MVC in 2008. Representing a total departure from the Web Forms approach, ASP.NET MVC abandons the page-based architecture completely, relying on the Model-View-Controller (MVC) architecture instead.
Note
Unlike ASP.NET Web Forms, which was introduced as a replacement to its predecessor, ASP, ASP.NET MVC does not in any way replace the existing Web Forms Framework. Quite the contrary—both ASP.NET MVC and Web Forms applications are built on top of the common ASP.NET Framework, which provides a common web API that both frameworks leverage quite heavily.
The idea that ASP.NET MVC and Web Forms are just different ways of making an ASP.NET website is a common theme throughout this book; in fact, both Chapter 2 and Appendix A explore this concept in depth.
The Model-View-Controller pattern is an architectural pattern that encourages strict isolation between the individual parts of an application. This isolation is better known as separation of concerns, or, in more general terms, “loose coupling.” Virtually all aspects of MVC—and, consequently, the ASP.NET MVC Framework—are driven by this goal of keeping disparate parts of an application isolated from each other.
Architecting applications in a loosely coupled manner brings a number of both short- and long-term benefits:
- Development
Individual components do not directly depend on other components, which means that they can be more easily developed in isolation. Components can also be readily replaced or substituted, preventing complications in one component from affecting the development of other components with which it may interact.
- Testability
Loose coupling of components allows test implementations to stand in for “production” components. This makes it easier to, say, avoid making calls to a database, by replacing the component that makes database calls with one that simply returns static data. The ability for components to be easily swapped with mock representations greatly facilitates the testing process, which can drastically increase the reliability of the system over time.
- Maintenance
Isolated component logic means that changes are typically isolated to a small number of components—often just one. Since the risk of change generally correlates to the scope of the change, modifying fewer components is a good thing!
The MVC pattern splits an application into three layers: the model, the view, and the controller (see Figure 1-1). Each of these layers has a very specific job that it is responsible for and—most important—is not concerned with how the other layers do their jobs.
The model represents
core business logic and data. Models encapsulate the
properties and behavior of a domain entity and expose properties that
describe the entity. For example, the Auction
class represents the concept of an
“auction” in the application and may expose properties such as Title
and CurrentBid
, as well as exposing behavior in
the form of methods such as Bid()
.
The view is responsible for transforming a model or models into a visual representation. In web applications, this most often means generating HTML to be rendered in the user’s browser, although views can manifest in many forms. For instance, the same model might be visualized in HTML, PDF, XML, or perhaps even in a spreadsheet.
Following separation of concerns, views should concentrate only on displaying data and should not contain any business logic themselves—the business logic stays in the model, which should provide the view with everything it needs.
This book explores the ASP.NET MVC Framework in depth, showing how to make the most of the features and functionality it offers. Since we’re now up to the fourth version of the framework, however, much of what the book covers is functionality that existed prior to this latest version. If you are already familiar with previous versions of the framework, you’re probably eager to skip over what you already know and begin learning all about the new additions.
The list below gives a brief description of each of the features new to version 4 of ASP.NET MVC, along with references pointing you to the sections of the book that show these features in action:
- Asynchronous controllers
Internet Information Server (IIS) processes each request it receives on a new thread, so each new request ties up one of the finite number of threads available to IIS, even if that thread is sitting idle (for example, waiting for a response from a database query or web service). And, while recent updates in .NET Framework 4.0 and IIS 7 have drastically increased the default number of threads available to the IIS thread pool, it’s still a good practice to avoid holding on to system resources for longer than you need to. Version 4 of the ASP.NET MVC Framework introduces asynchronous controllers to better handle these types of long-running requests in a more asynchronous fashion. Through the use of asynchronous controllers, you can tell the framework to free up the thread that is processing your request, letting it perform other processing tasks while it waits for the various tasks in the request to finish. Once they finish, the framework picks up where it left off, and returns the same response as if the request had gone through a normal synchronous controller—except now you can handle many more requests at once! If you’re interested in learning more about asynchronous controllers, see Chapter 11, which explains them in depth.
- Display modes
A growing number of devices are Internet-connected and ready to surf your site, and you need to be ready for them. Many times, the data displayed on these devices is the same as the data displayed on desktop devices, except the visual elements need to take into consideration the smaller form factor of mobile devices. ASP.NET MVC display modes provide an easy, convention-based approach for tailoring views and layouts to target different devices. Chapter 10 shows how to apply display modes to your site as part of a holistic approach to adding mobile device support to your sites.
- Bundling and minification
Even though it may seem like the only way to get on the Internet these days is through some sort of high-speed connection, that doesn’t mean you can treat the client-side resources that your site depends on in a haphazard manner. In fact, when you consider how the overall download times are increasing, wasting even fractions of a second in download times can really add up and begin to have a very negative effect on the perceived performance of your site. Concepts such as script and stylesheet combining and minification may not be anything new, but with the .NET Framework 4.5 release, they are now a fundamental part of the framework. What’s more, ASP.NET MVC embraces and extends the core .NET Framework functionality to make this tooling even more usable in your ASP.NET MVC applications. Chapter 13 helps you tackle all of these concepts and also shows you how to use the new tooling offered in the core ASP.NET and ASP.NET MVC Frameworks.
- Web API
Simple HTTP data services are rapidly becoming the primary way to supply data to the ever-increasing variety of applications, devices, and platforms. ASP.NET MVC has always provided the ability to return data in various formats, including JSON and XML; however, the ASP.NET Web API takes this interaction a step further, providing a more modern programming model that focuses on providing full-fledged data services rather than controller actions that happen to return data. In Chapter 6, you’ll see how to really take advantage of AJAX on the client—and you’ll use ASP.NET Web API services to do it!
This book aims to show you not only the ins and outs of the ASP.NET MVC Framework, but also how to leverage the framework in real-world applications. The problem with such applications is that the very meaning of “real-world” indicates a certain level of complexity and uniqueness that can’t be adequately represented in a single demo application.
Instead of attempting to demonstrate solutions to every problem you may face, we—the authors of this book—have assembled a list of the scenarios and issues that we have most frequently encountered and that we most frequently hear of others encountering. Though this list of scenarios may not include every scenario you’ll face while developing your application, we believe it represents the majority of the real-world problems that most developers face over the course of creating their ASP.NET MVC applications.
Note
We’re not kidding, we actually wrote a list—and it’s in the back of this book! Appendix D has a cross-referenced list of all the features and scenarios we cover and the chapter(s) in which we cover them.
In order to cover the scenarios on this list, we came up with a web application that combines them all into as close to a real-world application as we could get, while still limiting the scope to something everyone understands: an online auction site.
Introducing EBuy, the online auction site powered by ASP.NET MVC! From a high level, the goals of the site are pretty straightforward: allow users to list items they wish to sell, and bid on items they wish to buy. As you take a deeper look, however, you’ll begin to see that the application is a bit more complex than it sounds, requiring not only everything ASP.NET MVC has to offer, but also integration with other technologies.
EBuy is not just a bunch of code that we ship along with the book, though. Each chapter of the book not only introduces more features and functionality, but uses them to build the EBuy application—from new project to deployed application, preferably while you follow along and write the code, too!
Note
OK, we’ll admit that EBuy is also “just a bunch of code.” In fact, you can download EBuy in its entirety from the book’s website: http://www.programmingaspnetmvc.com.
Now, let’s stop talking about an application that doesn’t exist yet and start building it!
In order to begin developing ASP.NET MVC applications, you’ll need to download and install the ASP.NET MVC 4 Framework. This is as easy as visiting the ASP.NET MVC website and clicking the Install button.
This launches the Web Platform Installer, a free tool that simplifies the installation of many web tools and applications. Follow the Web Platform Installer wizard to download and install ASP.NET MVC 4 and its dependencies to your machine.
Note that in order to install and use ASP.NET MVC 4, you must have at least PowerShell 2.0 and Visual Studio 2010 Service Pack 1 or Visual Web Developer Express 2010 Service Pack 1. Luckily, if you do not already have them installed, the Web Platform Installer should figure it out and proceed to download and install the latest versions of PowerShell and Visual Studio for you!
Note
If you are currently using the previous version of ASP.NET MVC and would like to both create ASP.NET MVC 4 applications and continue working with ASP.NET MVC 3 applications, fear not—ASP.NET MVC can be installed and run side by side with ASP.NET MVC 3 installations.
Once you’ve gotten everything installed, it’s time to proceed to the next step: creating your first ASP.NET MVC 4 application.
The ASP.NET MVC 4 installer adds a new Visual Studio project type named ASP.NET MVC 4 Web Application. This is your entry point to the world of ASP.NET MVC and is what you’ll use to create the new EBuy web application project that you’ll build on as you progress through this book.
To create a new project, select the Visual C# version of the ASP.NET MVC 4 Web Application template and enter Ebuy.Website into the Name field (see Figure 1-2).
When you click OK to continue, you’ll be presented with another dialog with more options (see Figure 1-3).
This dialog lets you customize the ASP.NET MVC 4 application that Visual Studio is going to generate for you by letting you specify what kind of ASP.NET MVC site you want to create.
To begin, ASP.NET MVC 4 offers several project templates, each of which targets a different scenario:
- Empty
The Empty template creates a bare-bones ASP.NET MVC 4 application with the appropriate folder structure that includes references to the ASP.NET MVC assemblies as well as some JavaScript libraries that you’ll probably use along the way. The template also includes a default view layout and generates a Global.asax file that includes the standard configuration code that most ASP.NET MVC applications will need.
- Basic
The Basic template creates a folder structure that follows ASP.NET MVC 4 conventions and includes references to the ASP.NET MVC assemblies. This template represents the bare minimum that you’ll need to begin creating an ASP.NET MVC 4 project, but no more—you’ll have to do all the work from here!
- Internet Application
The Internet Application template picks up where the Empty template leaves off, extending the Empty template to include a simple default controller (
Home
Controller
), anAccountController
with all the logic required for users to register and log in to the website, and default views for both of these controllers.- Intranet Application
The Intranet Application template is much like the Internet Application template, except that it is preconfigured to use Windows-based authentication, which is desirable in intranet scenarios.
- Mobile Application
The Mobile Application template is another variation of the Internet Application template. This template, however, is optimized for mobile devices and includes the jQuery Mobile JavaScript framework and views that apply the HTML that works best with jQuery Mobile.
- Web API
The Web API template is yet another variation of the Internet Application template that includes a preconfigured Web API controller. Web API is the new lightweight, RESTful HTTP web services framework that integrates quite nicely with ASP.NET MVC. Web API is a great choice for quickly and easily creating data services that your AJAX-enabled applications can easily consume. Chapter 6 covers this new API in great detail.
The New ASP.NET MVC Project dialog also lets you select a view engine, or syntax that your views will be written in. We’ll be using the new Razor syntax to build the EBuy reference application, so you can leave the default value (“Razor”) selected. Rest assured that you can change the view engine your application uses at any time—this option exists only to inform the wizard of the kind of views it should generate for you, not to lock the application into a specific view engine forever.
Finally, choose whether or not you’d like the wizard to generate a unit test project for this solution. Once again, you don’t have to worry about this decision too much—as with any other Visual Studio solution, you are able to add a unit test project to an ASP.NET MVC web application anytime you’d like.
When you’re happy with the options you’ve selected, click OK to have the wizard generate your new project!
To make website development easier and help developers be more productive, ASP.NET MVC relies on the concept of convention over configuration whenever possible. This means that, instead of relying on explicit configuration settings, ASP.NET MVC simply assumes that developers will follow certain conventions as they build their applications.
The ASP.NET MVC project folder structure (Figure 1-4) is a great example of the framework’s use of convention over configuration. There are three special folders in the project that correspond to the elements of the MVC pattern: the Controllers, Models, and Views folders. It’s pretty clear at a glance what each of these folders contains.
When you look at the contents of these folders, you’ll find even more conventions at work. For example, not only does the Controllers folder contain all of the application’s controller classes, but the controller classes all follow the convention of ending their names with the Controller suffix. The framework uses this convention to register the application’s controllers when it starts up and associate controllers with their corresponding routes.
Next, take a look at the Views folder. Beyond the obvious convention dictating that the application’s views should live under this folder, it is split into subfolders: a Shared folder, and an optional folder to contain the views for each controller. This convention helps save developers from providing explicit locations of the views they’d like to display to users. Instead, developers can just provide the name of a view—say, “Index”—and the framework will try its best to find the view within the Views folder, first in the controller-specific folder and then, failing that, in the Shared views folder.
At first glance, the concept of convention over configuration may seem trivial. However, these seemingly small or meaningless optimizations can really add up to significant time savings, improved code readability, and increased developer productivity.
Once your project is created, feel free to hit F5 to execute your ASP.NET MVC website and watch it render in your browser.
Congratulations, you’ve just created your first ASP.NET MVC 4 application!
After you’ve calmed down from the immense excitement you experience as a result of making words show up in a web browser, you might be left wondering, “What just happened? How did it do that?”
Figure 1-5 shows, from a high level, how ASP.NET MVC processes a request.
Though we’ll spend the rest of this book diving deeper and deeper into the components of that diagram, the next few sections start out by explaining those fundamental building blocks of ASP.NET MVC.
All ASP.NET MVC traffic starts out like any other website traffic: with a request to a URL. This means that, despite the fact that it is not mentioned anywhere in the name, the ASP.NET Routing framework is at the core of every ASP.NET MVC request.
In simple terms, ASP.NET routing is just a pattern-matching system. At startup, the application registers one or more patterns with the framework’s route table to tell the routing system what to do with any requests that match those patterns. When the routing engine receives a request at runtime, it matches that request’s URL against the URL patterns registered with it (Figure 1-6).
When the routing engine finds a matching pattern in its route table, it forwards the request to the appropriate handler for that request.
Otherwise, when the request’s URL does not match any of the registered route patterns, the routing engine indicates that it could not figure out how to handle the request by returning a 404 HTTP status code.
ASP.NET MVC routes are responsible for determining which controller method (otherwise known as a controller action) to execute for a given URL. They consist of the following properties:
- Unique name
A name may be used as a specific reference to a given route
- URL pattern
A simple pattern syntax that parses matching URLs into meaningful segments
- Defaults
An optional set of default values for the segments defined in the URL pattern
- Constraints
A set of constraints to apply against the URL pattern to more narrowly define the URLs that it matches
The default ASP.NET MVC project templates add a generic route that
uses the following URL convention to break the URL for a given request
into three named segments, wrapped with brackets ({}
): “controller”, “action”, and “id”:
{controller}/{action}/{id}
This route pattern is registered via a call to the MapRoute()
extension
method that runs during application startup (located in
App_Start/RouteConfig.cs):
routes
.
MapRoute
(
"Default"
,
// Route name
"{controller}/{action}/{id}"
,
// URL with parameters
new
{
controller
=
"Home"
,
action
=
"Index"
,
id
=
UrlParameter
.
Optional
}
// Parameter defaults
);
In addition to providing a name and URL pattern, this route also defines a set of default parameters to be used in the event that the URL fits the route pattern, but doesn’t actually provide values for every segment.
For instance, Table 1-1 contains a list of URLs that match this route pattern, along with corresponding values that the routing framework will provide for each of them.
Table 1-1. Values provided for URLs that match our route pattern
URL | Controller | Action | ID |
---|---|---|---|
/auctions/auction/1234 | | | |
/auctions/recent | | | |
/auctions | | | |
/ | | |
The first URL (/auctions/auction/1234) in the table is a perfect match because it satisfies every segment of the route pattern, but as you continue down the list and remove segments from the end of the URL, you begin to see defaults filling in for values that are not provided by the URL.
This is a very important example of how ASP.NET MVC leverages the
concept of convention over
configuration: when the application starts up, ASP.NET MVC
discovers all of the application’s controllers by searching through the
available assemblies for classes that implement the System.Web.Mvc.IController
interface (or derive from a class that implements this
interface, such as System.Web.Mvc.Controller
)
and whose class names end with the suffix
Controller. When the routing framework uses this
list to figure out which controllers it has access to, it chops off the
Controller suffix from all of the controller class
names. So, whenever you need to refer to a controller, you do so by its
shortened name, e.g., AuctionsController
is referred to as
Auctions, and HomeController
becomes
Home.
What’s more, the controller and action values in a route are not
case-sensitive. This means that each of these
requests—/Auctions/Recent,
/auctions/Recent,
/auctions/recent, or even
/aucTionS/rEceNt—will successfully resolve to the
Recent
action in the AuctionsController
.
Tip
URL route patterns are relative to the application root, so they do not need to start with a forward slash (/) or a virtual path designator (~/). Route patterns that include these characters are invalid and will cause the routing system to throw an exception.
As you may have noticed, URL routes can contain a wealth of information that the routing engine is able to extract. In order to process an ASP.NET MVC request, however, the routing engine must be able to determine two crucial pieces of information: the controller and the action. The routing engine can then pass these values to the ASP.NET MVC runtime to create and execute the specified action of the appropriate controller.
In the context of the MVC architectural pattern, a controller responds to user input (e.g., a user clicking a Save button) and collaborates between the model, view, and (quite often) data access layers. In an ASP.NET MVC application, controllers are classes that contain methods that are called by the routing framework to process a request.
To see an example of an ASP.NET MVC controller, take a look at the
HomeController
class found in Controllers/HomeController.cs:
using
System.Web.Mvc
;
namespace
Ebuy.Website.Controllers
{
public
class
HomeController
:
Controller
{
public
ActionResult
Index
()
{
ViewBag
.
Message
=
"Your app description page."
;
return
View
();
}
public
ActionResult
About
()
{
ViewBag
.
Message
=
"Your quintessential app description page."
;
return
View
();
}
public
ActionResult
Contact
()
{
ViewBag
.
Message
=
"Your quintessential contact page."
;
return
View
();
}
}
}
As you can see, controller classes themselves aren’t very special; that is, they don’t look much different from any other .NET class. In fact, it’s the methods in controller classes—referred to as controller actions—that do all the heavy lifting that’s involved in processing requests.
Tip
You’ll often hear the terms controller and controller action used somewhat interchangeably, even throughout this book. This is because the MVC pattern makes no differentiation between the two. However, the ASP.NET MVC Framework is mostly concerned with controller actions since they contain the actual logic to process the request.
For instance, the HomeController
class we just looked at
contains three actions: Index
,
About
, and Contact
. Thus, given the default route pattern
{controller}/{action}/{id}
, when a
request is made to the URL /Home/About, the routing
framework determines that it is the About()
method of the HomeController
class that should process the
request. The ASP.NET MVC Framework then
creates a new instance of the Home
Controller
class and executes its
About()
method.
In this case, the About()
method is pretty simple: it passes data to the view via the ViewBag
property (more on that later), and
then tells the ASP.NET MVC Framework to display the view named “About”
by calling the View()
method, which
returns an ActionResult
of type
ViewResult
.
It is very important to note that it is the controller’s job to tell the ASP.NET MVC Framework what it should do next, but not how to do it. This communication occurs through the use of +ActionResult+s, the return values which every controller action is expected to provide.
For example, when a controller decides to show a view, it tells
the ASP.NET MVC Framework to show the view by returning a ViewResult
. It does not render the view
itself. This loose coupling is another great example of separation of
concerns in action (what to do versus
how it should be done).
Despite the fact that every controller action needs to return an
ActionResult
, you will rarely be
creating them manually. Instead, you’ll usually rely on the helper
methods that the System.Web.Mvc.Controller
base class provides,
such as:
Content()
Returns a
ContentResult
that renders arbitrary text, e.g., “Hello, world!”File()
Returns a
FileResult
that renders the contents of a file, e.g., a PDF.HttpNotFound()
Returns an
HttpNotFoundResult
that renders a 404 HTTP status code response.JavaScript()
:: Returns aJavaScriptResult
that renders JavaScript, e.g., “function hello() { alert(Hello, World!); }”.
Json()
Returns a
JsonResult
that serializes an object and renders it in JavaScript Object Notation (JSON) format, e.g., “{ “Message”: Hello, World! }”.PartialView()
Returns a
PartialViewResult
that renders only the content of a view (i.e., a view without its layout).Redirect()
Returns a
RedirectResult
that renders a 302 (temporary) status code to redirect the user to a given URL, e.g., “302 http://www.ebuy.com/auctions/recent”. This method has a sibling,RedirectPermanent()
, that also returns aRedirectResult
, but uses HTTP status code 301 to indicate a permanent redirect rather than a temporary one.RedirectToAction()
andRedirectToRoute()
Act just like the
Redirect()
helper, only the framework dynamically determines the external URL by querying the routing engine. Like theRedirect()
helper, these two helpers also have permanent redirect variants:RedirectToActionPermanent()
andRedirectToRoutePermanent()
.View()
As you can tell from this list, the framework provides an action result for just about any situation you need to support, and, if it doesn’t, you are free to create your own!
Tip
Though all controller actions are required to provide an
ActionResult
that indicates the
next steps that should be taken to process the request, not all
controller actions need to specify ActionResult
as their return type.
Controller actions can specify any return type that derives from
ActionResult
, or even any other
type.
When the ASP.NET MVC Framework comes across a controller action
that returns a non-ActionResult
type, it automatically wraps the value in a ContentResult
and renders the value as raw
content.
Controller actions are—when it comes down to it—just like any other method. In fact, a controller action can even specify parameters that ASP.NET MVC populates, using information from the request, when it executes. This functionality is called model binding, and it is one of ASP.NET MVC’s most powerful and useful features.
Before diving into how model binding works, first take a step back and consider an example of the “traditional” way of interacting with request values:
public
ActionResult
Create
()
{
var
auction
=
new
Auction
()
{
Title
=
Request
[
"title"
],
CurrentPrice
=
Decimal
.
Parse
(
Request
[
"currentPrice"
]),
StartTime
=
DateTime
.
Parse
(
Request
[
"startTime"
]),
EndTime
=
DateTime
.
Parse
(
Request
[
"endTime"
]),
};
// ...
}
The controller action in this particular example creates and
populates the properties of a new Auction
object with values taken straight from
the request. Since some of Auction
’s
properties are defined as various primitive, non-string
types, the action also needs to parse
each of those corresponding request values into the proper type.
This example may seem simple and straightforward, but it’s
actually quite frail: if any of the parsing attempts fails, the entire
action will fail. Switching to the various TryParse()
methods may help avoid most
exceptions, but applying these methods also means additional
code.
The side effect of this approach is that every action is very
explicit. The downside to writing such explicit code is that it puts the
burden on you, the developer, to perform all the work and to remember to
perform this work every time it is required. A larger amount of code
also tends to obscure the real goal: in this example, adding a new
Auction
to the system.
Not only does model binding avoid all of this explicit code, it is also very easy to apply. So easy, in fact, that you don’t even need to think about it.
For example, here’s the same controller action as before, this time using model-bound method parameters:
public
ActionResult
Create
(
string
title
,
decimal
currentPrice
,
DateTime
startTime
,
DateTime
endTime
)
{
var
auction
=
new
Auction
()
{
Title
=
title
,
CurrentPrice
=
currentPrice
,
StartTime
=
startTime
,
EndTime
=
endTime
,
};
// ...
}
Now, instead of retrieving the values from the Request
explicitly, the action declares them
as parameters. When the ASP.NET MVC framework executes this method, it
attempts to populate the action’s parameters using the same values
from the request that the previous example showed. Note that—even
though we’re not accessing the Request
dictionary directly—the parameter
names are still very important, because they still correspond to
values from in the Request
.
The Request
object isn’t the
only place the ASP.NET MVC model binder gets its values from, however.
Out of the box, the framework looks in several places, such as route
data, query string parameters, form post values, and even serialized
JSON objects. For example, the following snippet retrieves the
id
value from the URL simply by
declaring a parameter with the same name:
Example 1-1. Retrieving the id from a URL (e.g. /auctions/auction/123)
public
ActionResult
Auction
(
long
id
)
{
var
context
=
new
EBuyContext
();
var
auction
=
context
.
Auctions
.
FirstOrDefault
(
x
=>
x
.
Id
==
id
);
return
View
(
"Auction"
,
auction
);
}
Tip
Where and how the ASP.NET MVC model binder finds these values is actually quite configurable and even extensible. See Chapter 8 for an in-depth discussion of ASP.NET MVC model binding.
As these examples demonstrate, model binding lets ASP.NET MVC handle much of the mundane, boilerplate code so the logic within the action can concentrate on providing business value. The code that is left is much more meaningful, not to mention more readable.
Applying the model binding approach even to simple, primitive types can make a pretty big impact in making your code more expressive. In the real world, though, things are much more complex—only the most basic scenarios rely on just a couple of parameters. Luckily, ASP.NET MVC supports binding to complex types as well as to primitive types.
This example takes one more pass at the Create
action, this time skipping the
middleman primitive types and binding directly to an Auction
instance:
public
ActionResult
Create
(
Auction
auction
)
{
// ...
}
The action shown here is equivalent to what you saw in the
previous example. That’s right—ASP.NET MVC’s complex model binding
just eliminated all of the boilerplate code
required to create and populate a new Auction
instance! This example shows the
true power of model binding.
Action filters provide a simple yet powerful technique to modify or enhance the ASP.NET MVC pipeline by “injecting” logic at certain points, helping to address “cross-cutting concerns” that apply to many (or all) components of an application. Application logging is a classic example of a cross-cutting concern in that it is equally applicable to any component in an application, regardless of what that component’s primary responsibility may be.
Action filter logic is primarily introduced by applying an ActionFilterAttribute
to a controller action
in order to affect how that action executes, as is the case in the
following example that protects a controller action from unauthorized
access by applying the AuthorizeAttribute
:
[Authorize]
public
ActionResult
Profile
()
{
// Retrieve profile information for current user
return
View
();
}
The ASP.NET MVC Framework includes quite a few action filters that target common scenarios. You’ll see these action filters in use throughout this book, helping accomplish a variety of tasks in a clean, loosely coupled way.
Note
Action filters are a great way to apply custom logic throughout
your site. Keep in mind that you are free to create your own action
filters by extending the ActionFilterAttribute
base class or any of
the ASP.NET MVC action filters.
In the ASP.NET MVC Framework, controller actions that wish to display HTML to the user
return an instance of ViewResult
, a
type of ActionResult
that knows how to render content to the response. When it comes
time to render the view, the ASP.NET MVC Framework will look for the view
using the name provided by the controller.
Take the Index
action in the
HomeController
:
public
ActionResult
Index
()
{
ViewBag
.
Message
=
"Your app description page."
;
return
View
();
}
This action takes advantage of the View()
helper method to create a ViewResult
. Calling View()
without any parameters, as in this
example, instructs ASP.NET MVC to find a view with the same name as the
current controller action. In this instance, ASP.NET MVC will look for a
view named “Index”, but where will it look?
ASP.NET MVC relies on the convention that keeps all the application’s views underneath the Views folder in the root of the website. More specifically, ASP.NET MVC expects views to live within folders named after the controller to which they relate.
Thus, when the framework wants to show the view for the Index
action in the HomeController
, it is going to look in the
/Views/Home folder for a file named
Index. The screenshot in Figure 1-7 shows that the project template was
nice enough to include an Index.cshtml view for
us.
When it does not find a view that matches the name of the view it is looking for in the controller’s Views folder, ASP.NET MVC continues looking in the common /Views/Shared folder.
Note
The /Views/Shared folder is a great place to keep views that are shared across multiple controllers.
Now that you’ve found the view that the action requested, open it up and take a look at what’s inside: HTML markup and code. But, it’s not just any HTML markup and code—it’s Razor!
Razor is a syntax that allows you to combine code and content in a fluid and expressive manner. Though it introduces a few symbols and keywords, Razor is not a new language. Instead, Razor lets you write code using languages you probably already know, such as C# or Visual Basic .NET.
Razor’s learning curve is very short, because it lets you work with your existing skills rather than requiring you to learn an entirely new language. Therefore, if you know how to write HTML and .NET code using C# or Visual Basic .NET, you can easily write markup such as the following:
<div>
This page rendered at @DateTime.Now</div>
Which produces the following output:
<div>This page rendered at 12/7/1941 7:38:00 AM</div>
This example begins with a standard HTML tag (the <div>
tag), followed by a bit of
“hardcoded” text, then a bit of dynamic text rendered as the result of
referencing a .NET property (System.DateTime.Now
), followed by the closing
(</div>
) tag.
Razor’s intelligent parser allows developers to be more expressive with their logic and make easier transitions between code and markup. Though Razor’s syntax might be different from other markup syntaxes (such as the Web Forms syntax), it’s ultimately working toward the same goal: rendering HTML.
To illustrate this point, take a look at the following snippets that show examples of common scenarios implemented in both Razor markup and Web Forms markup.
Here is an if/else
statement
using Web Forms syntax:
<% if(User.IsAuthenticated) { %> <span>Hello, <%: User.Username %>!</span> <% } %> <% else { %> <span>Please <%: Html.ActionLink("log in") %></span> <% } %>
and using Razor syntax:
@if(User.IsAuthenticated) { <span>Hello, @User.Username!</span> } else { <span>Please @Html.ActionLink("log in")</span> }
And here is a foreach
loop
using Web Forms syntax:
<ul> <% foreach( var auction in auctions) { %> <li><a href="<%: auction.Href %>"><%: auction.Title %></a></li> <% } %> </ul>
and using Razor syntax:
<ul> @foreach( var auction in auctions) { <li><a href="@auction.Href">@auction.Title</a></li> } </ul>
Though they use a different syntax, the two snippets for each of the examples render the same HTML.
Razor provides two ways to differentiate code from markup: code nuggets and code blocks.
Code nuggets are simple expressions that are evaluated and rendered inline. They can be mixed with text and look like this:
Not
Logged
In
:
@Html
.
ActionLink
(
"Login"
,
"Login"
)
The expression begins immediately after the @
symbol, and Razor
is smart enough to know that the closing parenthesis indicates the end
of this particular statement.
The previous example will render this output:
Not Logged In: <a href="/Login">Login</a>
Notice that code nuggets must always return markup for the view
to render. If you write a code nugget that evaluates to a void
value, you will receive an error when
the view executes.
A code block is a
section of the view that contains strictly code rather
than a combination of markup and code. Razor defines code blocks as
any section of a Razor template wrapped in @{
}
characters. The @{
characters mark the beginning of the block, followed by any number of
lines of fully formed code. The }
character closes the code block.
Keep in mind that the code within a code block is not like code
in a code nugget. It is regular code that must follow the rules of the
current language. For example, each line of code written in C# must
include a semicolon (;
) at the
end, just as if it lived within a class in a .cs
file.
Here is an example of a typical code block:
@
{
LayoutPage
=
"~/Views/Shared/_Layout.cshtml"
;
View
.
Title
=
"Auction "
+
Model
.
Title
;
}
Code blocks do not render anything to the view. Instead, they allow you to write arbitrary code that requires no return value.
Also, variables defined within code blocks may be used by code
nuggets in the same scope. That is, variables defined within the scope
of a foreach
loop or similar
container will be accessible only within that container, while
variables that are defined at the top level of a view (not in any kind
of container) will be accessible to any other code blocks or code
nuggets in that same view.
To better clarify this, take a look at a view with a few variables defined at different scopes:
@{ // The title and bids variables are // available to the entire view var title = Model.Title; var bids = Model.Bids; }<h1>
@title<h1>
<div
class=
"items"
>
<!-- Loop through the objects in the bids variable -->
@foreach(var bid in bids) {<!-- The bid variable is only available within the foreach loop -->
<div
class=
"bid"
>
<span
class=
"bidder"
>
@bid.Username</span>
<span
class=
"amount"
>
@bid.Amount</span>
</div>
}<!-- This will throw an error: the bid variable does not exist at this scope! -->
<div>
Last Bid Amount: @bid.Amount</div>
</div>
Code blocks are a means to execute code within a template and do not render anything to the view. In direct contrast to the way that code nuggets must provide a return value for the view to render, the view will completely ignore values that a code block returns.
Razor offers the ability to maintain a consistent look and feel throughout your entire website through layouts. With layouts, a single view acts as a template for all other views to use, defining the site-wide page layout and style.
A layout template typically includes the primary markup (scripts, CSS stylesheets, and structural HTML elements such as navigation and content containers), specifying locations within the markup in which views can define content. Each view in the site then refers to this layout, including only the content within the locations the layout has indicated.
Take a look at a basic Razor layout file (_Layout.cshtml):
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"utf-8"
/>
<title>
@View.Title</title>
</head>
<body>
<div
class=
"header"
>
@RenderSection("Header")</div>
@RenderBody()<div
class=
"footer"
>
@RenderSection("Footer")</div>
</body>
</html>
The layout file contains the main HTML content, defining the HTML
structure for the entire site. The layout relies on variables (such as
@View.Title
) and helper functions
like @RenderSection([Section Name])
and @RenderBody()
to interact with
individual views.
Once a Razor layout is defined, views reference the layout and supply content for the sections defined within the layout.
The following is a basic content page that refers to the previously defined _Layout.cshtml file:
@{ Layout = "~/_Layout.cshtml"; } @section Header {<h1>
EBuy Online Auction Site<h1>
} @section Footer { Copyright @DateTime.Now.Year }<div
class=
"main"
>
This is the main content.</div>
Razor layouts and the content views that depend on them are assembled together like puzzle pieces, each one defining one or more portions of the entire page. When all the pieces get assembled, the result is a complete web page.
While layouts offer a helpful way to reuse portions of markup and maintain a consistent look and feel throughout multiple pages in your site, some scenarios may require a more focused approach.
The most common scenario is needing to display the same high-level information in multiple locations in a site, but only on a few specific pages and in different places on each of those pages.
For instance, the Ebuy auction site may render a list of compact auction details—showing only the auction’s title, current price, and perhaps a thumbnail of the item—in multiple places in the site such as the search results page as well as a list of featured auctions on the site’s homepage.
ASP.NET MVC supports these kinds of scenarios through partial views.
Partial views are views that contain targeted markup designed to be rendered as part of a larger view. The following snippet demonstrates a partial view to display the compact auction details mentioned in the scenario above:
@model Auction <div class="auction"> <a href="@Model.Url"> <img src="@Model.ImageUrl" /> </a> <h4><a href="@Model.Url">@Model.Title</a></h4> <p>Current Price: @Model.CurrentPrice</p> </div>
To render this snippet as a partial view, simply save it as its
own standalone view file (e.g.
/Views/Shared/Auction.cshtml) and use one of
ASP.NET MVC’s HTML Helpers—Html.Partial()
—to render
the view as part of another view.
To see this in action, take a look at the following snippet, which iterates over a collection of auction objects and uses the partial view above to render the HTML for each auction:
@model IEnumerable<Auction> <h2>Search Results</h2> @foreach(var auction in Model) { @Html.Partial("Auction", auction) }
Notice that the first parameter to the
Html.Partial()
helper method is a string containing
the name of the view without its extension.
This is because the Html.Partial()
helper
method is just a simple layer on top of ASP.NET MVC’s powerful view
engine, which renders the view very similar to what occurs after a
controller action calls the View()
method to return a
view action result: the engine uses the view name to locate and execute
the appropriate view.
In this way, partial views are developed and executed almost exactly like any other kind of view. The only difference is that they are designed to be rendered as part of a larger view.
The second parameter (auction
in the example
above) accepts the partial view’s model, just like the model parameter
in the View(_View Name_, _[Model]_)
controller helper
method. This second model parameter is optional; when it’s not
specified, it defaults to the model in the view from which the
Html.Partial()
helper was called. For instance, if
the second auction
parameter were omitted in the
example above, ASP.NET MVC would pass the view’s
Model
property (of type
IEnumerable<Auction>
) in its place.
Note
The examples above show how partial views can provide reusable sections of markup that can help reduce duplication and complexity in your views.
Though useful, this is only one way to take advantage of partial views—Partial Rendering shows how to take advantage of partial views to provide a simple and effective way to enhance your site with AJAX.
The MVC architecture depends on the model, view, and controller all remaining separate and distinct, while still working together to accomplish a common goal. In this relationship, it is the controller’s job to be the “traffic cop,” coordinating various parts of the system to execute the application’s logic. This processing typically results in some kind of data that needs to be relayed to the user. Alas, it is not the controller’s job to display things to the user—that is what views are for! The question then becomes, how does the controller communicate this information to the view?
ASP.NET MVC offers two ways to communicate data across
model-view-controller boundaries: ViewData
and TempData
. These objects are
dictionaries available as properties in both controllers and views.
Thus, passing data from a controller to a view can be as simple as
setting a value in the controller, as in this snippet from
HomeController.cs:
public
ActionResult
About
()
{
ViewData
[
"Username"
]
=
User
.
Identity
.
Username
;
ViewData
[
"CompanyName"
]
=
"EBuy: The ASP.NET MVC Demo Site"
;
ViewData
[
"CompanyDescription"
]
=
"EBuy is the world leader in ASP.NET MVC demoing!"
;
return
View
(
"About"
);
}
and referencing the value in the view, as in this portion of the About.cshtml file:
<h1>
@ViewData["CompanyName"]</h1>
<div>
@ViewData["CompanyDescription"]</div>
ASP.NET MVC controllers and views that expose
the ViewData
property also expose a
similar property named ViewBag
. The
ViewBag
property is simply a wrapper around the ViewData
that exposes the ViewData
dictionary as a dynamic
object.
For example, any references to values in the ViewData
dictionary in the preceding
snippets can be replaced with references to dynamic
properties on the ViewBag
object, as in:
public
ActionResult
About
()
{
ViewBag
.
Username
=
User
.
Identity
.
Username
;
ViewBag
.
CompanyName
=
"EBuy: The ASP.NET MVC Demo Site"
;
ViewBag
.
CompanyDescription
=
"EBuy is the world leader in ASP.NET MVC demoing!"
;
return
View
(
"About"
);
}
and:
<h1>@ViewBag.CompanyName</h1> <div>@ViewBag.CompanyDescription</div>
In addition to its basic dictionary
behavior, the ViewData
object also
offers a Model
property, which
represents the primary object that is the target of the request.
Though the ViewData.Model
property
is conceptually no different from ViewData["Model"]
, it promotes the model to
a first-class citizen and recognizes it as more important than the
other data that might be in the request.
For example, the previous two snippets showed that the CompanyName
and CompanyDescription
dictionary values are
clearly related to each other and represent a great opportunity to wrap together in a
model.
Take a look at CompanyInfo.cs:
public
class
CompanyInfo
{
public
string
Name
{
get
;
set
;
}
public
string
Description
{
get
;
set
;
}
}
the About
action in
HomeController.cs:
public
ActionResult
About
()
{
ViewBag
.
Username
=
User
.
Identity
.
Username
;
var
company
=
new
CompanyInfo
{
Name
=
"EBuy: The ASP.NET MVC Demo Site"
,
Description
=
"EBuy is the world leader in ASP.NET MVC demoing!"
,
};
return
View
(
"About"
,
company
);
}
and this snippet from About.cshtml:
@{ var company = (CompanyInfo)ViewData.Model; }<h1>
@company.Name</h1>
<div>
@company.Description</div>
In these snippets, the references to the CompanyName
and CompanyDescription
dictionary values have
been merged into an instance of a new class named CompanyInfo
(company
). The updated HomeController.cs snippet also shows an
overload of the View()
helper
method in action. This overload continues to accept the name of the
desired view as the first parameter. The second parameter, however,
represents the object that will be assigned to the ViewData.Model
property.
Now, instead of setting the dictionary values directly, company
is passed as the model
parameter to the View()
helper method and the view (About.cshtml) can get a local reference to
the company
object and access its
values.
By default, the Model
property available within Razor views
is dynamic
, which means that you
are able to access its values without needing to know its exact
type.
However, given the static nature of the C# language and Visual Studio’s excellent IntelliSense support for Razor views, it is often beneficial to specify the type of the page’s model explicitly.
Luckily, Razor makes this pretty easy—simply use the @model
keyword to indicate the model’s type name:
@model Auction<h1>
@Model.Name</h1>
<div>
@Model.Description</div>
This example modifies the previous Auction.cshtml example, avoiding the need
to add an intermediary variable to cast the ViewData.Model
into. Instead, the first line
uses the @model
keyword to indicate
that the model’s type is CompanyInfo
, making all references to
ViewData.Model
strongly typed and
directly accessible.
The primary goal of most web requests is to deliver HTML to the user,
and as such, ASP.NET MVC goes out of its way to help you create HTML. In
addition to the Razor markup language, ASP.NET MVC also offers many
helpers to generate HTML simply and effectively. The two most important
of these helpers are the HtmlHelper
and UrlHelper
classes, exposed in
controllers and views as the Html
and
Url
properties, respectively.
Here are some examples of the two helpers in action:
<img
src=
'@Url.Content("~/Content/images/header.jpg")'
/>
@Html.ActionLink("Homepage", "Index", "Home")
The rendered markup looks like this:
<img
src=
'/vdir/Content/images/header.jpg'
/>
<a
href=
"/vdir/Home/Index"
>
Homepage</a>
For the most part, the HtmlHelper
and UrlHelper
types don’t have many methods of
their own and are merely shims that the framework attaches behaviors to
via extension methods. This makes them an important extensibility point,
and you’ll see references to the two types throughout this book.
Though there are far too many methods to list in this section, the
one thing to take away at this point is: the HtmlHelper
class helps you generate HTML
markup and the UrlHelper
class helps
you generate URLs. Keep this in mind, and turn to these helpers anytime
you need to generate URLs or HTML.
Now that we’ve covered controllers and views, it’s time to complete the definition of MVC by discussing models, which are usually considered the most important part of the MVC architecture. If they are so important, why are they the last to be explained? Well, the model layer is notoriously difficult to explain because it is the layer that contains all of the business logic for the application—and that logic is different for every application.
From a more technical standpoint, the model typically consists of normal classes that expose data in the form of properties and logic in the form of methods. These classes come in all shapes and sizes, but the most common example is the “data model” or “domain model,” whose primary job is to manage data.
For example, take a look at the following snippet, which shows the
Auction
class—the model that will drive
the entire EBuy reference application:
public
class
Auction
{
public
long
Id
{
get
;
set
;
}
public
string
Title
{
get
;
set
;
}
public
string
Description
{
get
;
set
;
}
public
decimal
StartPrice
{
get
;
set
;
}
public
decimal
CurrentPrice
{
get
;
set
;
}
public
DateTime
StartTime
{
get
;
set
;
}
public
DateTime
EndTime
{
get
;
set
;
}
}
Though we will add various functionality such as validation and
behavior to the Auction
class
throughout this book, this snippet is still very representative of a model
in that it defines the data that makes up an “auction.”
And, just as we will build on the Auction
class throughout the book, be on
the lookout for more kinds of classes (such as services and helpers) that
all work together to make up the “Model” in “MVC.”
So far we’ve described all the parts that make up an ASP.NET MVC application, but the discussion has focused on the code that Visual Studio generates for us as part of the project template. In other words, we haven’t actually made anything yet. So let’s change that!
This section will focus on how to implement a feature from scratch, creating everything you need to accomplish an example scenario: displaying an auction. As a recap, every ASP.NET MVC request requires at least three things: a route, a controller action, and a view (and, optionally, a model).
To figure out the routing pattern that you’d like to use for a given feature, you must first determine what you’d like your URL for that feature to look like. In this example we are going to choose a relatively standard URL of Auctions/Details/[Auction ID]; for example, http://www.ebuy.biz/Auctions/Details/1234.
What a nice surprise—the default route configuration already supports this URL!
Next, we’ll need to create a controller to host the actions that will process the request.
Since controllers are merely classes that implement the ASP.NET
MVC controller interface, you
could manually add a new class to the
Controllers folder that derives from Syste
m.Web.Mvc.Controller
and begin adding
controller actions to that class. However, Visual Studio offers a bit of
tooling to take most of the work out of creating new controllers: simply
right-click on the Controllers folder and choose
the Add > Controller...
menu
option, which will pop up the Add Controller dialog shown in Figure 1-8.
The Add Controller dialog begins by asking for the name of the new controller class
(in this case, we’ll call it AuctionsController
) and follows up by asking
which scaffolding template you’d like to use, along with providing a set
of options to give you a little bit of control over how ASP.NET MVC is
going to generate the new controller class.
The Add Controller dialog offers several different controller templates that can help you get started developing your controllers more quickly:
- Empty MVC controller
The default template (“Empty MVC controller”) is the simplest one. It doesn’t offer any customization options because, well, it’s too simple to have any options! It merely creates a new controller with the given name that has a single generated action named
Index
.- MVC controller with read/write actions and views, using Entity Framework
The “MVC controller with read/write actions and views, using Entity Framework” template is just as impressive as it sounds. This template starts with the same output as the “MVC controller with empty read/write actions” template (see below) and then kicks it up a notch, generating code to help you access objects stored in an Entity Framework context and even generating Create, Edit, Details, and Delete views for those objects! This template is a great kick-start when your project uses Entity Framework to access your data, and in some cases the code it generates may be all that you need to support the Read, Edit, Update, and Delete operations for that data.
- MVC controller with empty read/write actions
The next option—“MVC controller with empty read/write actions”—generates the same code as the “Empty MVC controller” template, but adds a few more actions that you’ll most likely need in order to expose standard “data access” operations:
Details
,Create
,Edit
, andDelete
.- API controller templates
The final three templates—“Empty API controller,” “API controller with empty read/write actions,” and “API controller with read/write actions and views, using Entity Framework”—are the Web API counterparts to the MVC controller templates of the same names. We will cover these templates in more detail when we discuss ASP.NET MVC’s Web API functionality in Chapter 6.
Note
An interesting thing to notice about the controller code that
Visual Studio generates is that the Index
and Details
actions each have only one method,
while the Create
, Edit
, and Delete
actions each have two overloads—one
decorated with an HttpPost
Attribute
, and one without.
This is because Create
,
Edit
, and Delete
all involve two requests in order
to complete: the first request returns the view that the user can
interact with to create the second request, which actually performs
the desired action (creating, editing, or deleting data). This is a
very common interaction on the Web, and you’ll see several examples
of it throughout this book.
Unfortunately, we have not yet reached the point in the book where we are able to use Entity Framework, so for now you can choose the “MVC controller with empty read/write actions” option and click Add to have Visual Studio generate the next controller class.
After Visual Studio is done creating the AuctionsController
, find the Details
action and update it so it creates a
new instance of the Auction
model
shown earlier and passes that instance to the view using the View(object model)
method.
Yes, this is a silly example. Normally, you’d retrieve this information from somewhere such as a database—and we will show you how to do just that in Chapter 4—but for this example, we are using hardcoded values:
public
ActionResult
Details
(
long
id
=
0
)
{
var
auction
=
new
Ebuy
.
Website
.
Models
.
Auction
{
Id
=
id
,
Title
=
"Brand new Widget 2.0"
,
Description
=
"This is a brand new version 2.0 Widget!"
,
StartPrice
=
1.00
m
,
CurrentPrice
=
13.40
m
,
StartTime
=
DateTime
.
Parse
(
"6-15-2012 12:34 PM"
),
EndTime
=
DateTime
.
Parse
(
"6-23-2012 12:34 PM"
),
};
return
View
(
auction
);
}
With a Details
controller action in place and providing data to a view, it’s time to
create that view.
As with the controller class in the previous section, you are free to manually add new views (and folders to store them in) directly to the Views folder; however, if you’re the type who prefers a bit more automation, Visual Studio offers yet another wizard to do the work of creating the views—and the folders they live in—for you.
To add a view using the Visual Studio wizard, simply right-click
anywhere within the code of the action in a controller and choose the
Add View option, which will display the Add View wizard (Figure 1-9). This is actually
quite similar to the Add Controller dialog you just used to generate the
AuctionsController
.
The Add View dialog starts off by asking what you’d like to call
the new view, defaulting to the name of the controller action from which
you triggered the dialog (e.g., Details when called
from the Details
action). Then, the
dialog allows you to choose the syntax (aka “View Engine”) that you’d
like to use when authoring the view. This value defaults to the syntax
you chose when you created the web project, but (as promised earlier)
you are free to switch between syntaxes if it suits you, perhaps using
Razor for some views and the “ASPX” Web Forms syntax for others.
As in the Add Controller dialog, the rest of the options in the Add View wizard have to do with the code and markup that Visual Studio is going to generate when it creates the new view. For example, you can choose to have a strongly typed view model (as discussed in Strongly typed views) by selecting the model type from the list of classes in your project, or typing the type name in yourself. If you choose a strongly typed view, the wizard also lets you choose a template (e.g., Edit, Create, Delete), which analyzes the model type and generates the appropriate form fields for that type.
This is a great way to get up and running quickly and can save you quite a bit of typing, so let’s take advantage of it by checking the “Create a strongly typed view” checkbox, choosing our Auction model from the “Model class” drop-down list, and selecting the Details scaffold template.
Tip
Visual Studio will only include in the “Model class” drop-down
classes that it has been able to compile successfully, so if you do
not see the Auction
class you
created earlier, try to compile the solution and then open the Add
View dialog again.
Finally, you’ll need to tell Visual Studio whether this view is a partial view or should refer to a layout. When you’re using the ASPX Web Forms syntax to author your pages and you choose the “Create as a partial view” option, Visual Studio will create it as a User Control (.ascx) rather than a full page (.aspx). When using the Razor syntax, however, the “Create as a partial view” option has very little effect—Visual Studio creates the same type of file (.cshtml or .vbhtml) for both partial views and full pages. In the case of Razor syntax, the only effect this checkbox has is on the markup that gets generated inside of the new view.
For the purposes of this demo, you can leave the defaults alone: “Create as a partial view” should remain unchecked, while “Use a layout or master page” should be checked, with the layout text box left empty (see Figure 1-10).
When you’re ready, click the Add button to have Visual Studio add
the new view to your project. After it’s finished, you will see that
Visual Studio has analyzed the Auction
model and generated the required HTML
markup—complete with references to HTML helpers such as Html.DisplayFor
—to display all of the Auction
fields.
At this point, you should be able to run your site, navigate to
the controller action (e.g.,
/auctions/details/1234), and see the details of the
Auction
object rendered in your
browser, as shown in Figure 1-11.
It sure isn’t pretty, but remember, the HTML that Visual Studio generates is just a starting point to help you save time. Once it’s generated, you can feel free to change it however you like.
Congratulations—you have just created your first controller action and view from scratch!
So far we’ve covered just about everything you need to know in order to create an ASP.NET MVC application, but there is one more very important concept that you should know about before you continue with the rest of the book: how to protect your site by requiring users to authenticate themselves before they can access certain controller actions.
You may have noticed that the Internet Application template
generates an AccountController
—along
with some views to support it—which provides a full implementation of
forms authentication right out of the box. This is an acknowledgment that
security and authentication are crucial to just about every web
application and that, at some point, you’ll probably want to lock down
some or all of your site to restrict access to specific users (or groups
of users) and prevent unauthorized access by all other visitors. So, since
you’ll most likely need it anyway, why shouldn’t Visual Studio generate
the controller and views to get you started?
The traditional tactic used to lock down ASP.NET applications is to apply authorization settings to specific pages or directories via web.config configuration settings. Unfortunately, this approach does not work in ASP.NET MVC applications because ASP.NET MVC applications rely on routing to controller actions, not to physical pages.
Instead, the ASP.NET MVC Framework provides the AuthorizeAttribute
,
which can be applied to individual controller actions—or even entire
controllers—to restrict access only to authenticated users or,
alternatively, to specific users and user roles.
Take a look at the Profile
action
on the UsersController
that we’ll
create later in the book, which displays the profile information for the
current user:
public
class
UsersController
{
public
ActionResult
Profile
()
{
var
user
=
_repository
.
GetUserByUsername
(
User
.
Identity
.
Name
);
return
View
(
"Profile"
,
user
);
}
}
Clearly, this action will fail if the user is not logged in.
Applying the AuthorizeAttribute
to this
controller action causes any requests made to this action by users who are
not authenticated to be rejected:
public
class
UsersController
{
[Authorize]
public
ActionResult
Profile
()
{
var
user
=
_repository
.
GetUserByUsername
(
User
.
Identity
.
Name
);
return
View
(
"Profile"
,
user
);
}
}
If you’d like to be even more specific about the users who can
access the controller action, the AuthorizeAttribute
exposes the Users
property, which accepts a comma-delimited
whitelist of acceptable usernames, as well as the Roles
property, which accepts a list of allowed roles.
Now, when nonauthenticated users attempt to access this URL, they
will instead be redirected to the login URL: the Login
action on the AccountController
.
In order to help you get a jump-start on your application, the ASP.NET MVC
Internet Application project template includes the
AccountController
, which contains
controller actions that leverage the ASP.NET Membership
Providers.
The AccountController
provides
quite a bit of functionality out of the box, along with the views to
support each controller action. This means that your brand new ASP.NET
MVC application already contains the following fully implemented
features, without any coding on your part:
Login
Logoff
New user registration
Change password
Thus, when you apply the AuthorizeAttribute
to any of your controller
actions, users are redirected to the existing login page that the
project template creates for you (see Figure 1-12).
And, when users need to create a new account in order to log in, they can click the Register link to view the prebuilt Registration page (Figure 1-13).
Plus, if you don’t like the out-of-the-box views, they are easily customizable to meet your needs.
As this section shows, not only does the ASP.NET MVC Framework make it very easy to protect controller actions, but the default project template implements just about everything users will need to authenticate themselves on your site!
ASP.NET MVC leverages the time-tested Model-View-Controller architecture pattern to provide a website development framework that encourages loosely coupled architecture and many other popular object-oriented programming patterns and practices.
The ASP.NET MVC Framework gets you on the ground running right from the start with helpful project templates and a “convention over configuration” approach that cuts down on the amount of configuration required to create and maintain your application, freeing up more of your time so you can be more productive in getting your application completed and out the door.
This chapter introduced you to the fundamental concepts and basic skills that you need in order to get up and running building ASP.NET MVC 4 applications. The rest of this book will expand on this foundation, showing more features that the ASP.NET MVC Framework has to offer to help you build robust, maintainable web applications using the MVC architectural pattern.
So, what are you waiting for? Keep reading and learn everything you need to know to build the greatest web applications you’ve ever written!
Get Programming ASP.NET MVC 4 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.