21 September 2011

An Introduction to ASP.NET MVC Extensibility

Because ASP.NET MVC has been designed with extensibility as its design principle; almost every logical step of the processing pipeline can be replaced with your own implementation. In fact, the best way to develop applications with ASP.NET MVC is to extend the system, Simone starts a series that explains how to implement extensions to ASP.NET MVC, starting with the ones at the beginning of the pipeline (routing extensions) and finishing with the view extensions points.

If you are not extending, you are not doing it right: this is the main tenet that you must keep in mind while developing web applications with ASP.NET MVC.

I admit this is a strong statement, but over the next few months I’m going to explain why extending is the right way to develop with ASP.NET MVC, together with explaining when and where you can extend it, and, most importantly, how to do it. But not just yet: to start with, we will look at how a request is processed by the framework, and where during this processing you can replace the default behavior or inject your own logic. This obviously underpins everything we’ll be covering in later articles, so let’s make sure we understand this clearly.

Prerequisites

Given that we will be discussing several advanced topics in the coming months, it’s worth making sure you have a working knowledge of object oriented programming, .NET, ASP.NET, and the basics of ASP.NET MVC. All of the code samples I give you will be written in C#, so you’ll need to be comfortable with this language as well. The code samples will also be developed on .NET 4 and Visual Studio 2010.

What is extensibility?

The English dictionary defines extensibility as:

“the capability of being extended”

In computer science terms, a software system is extensible when it can be expanded without the need for changing and recompiling the original source code. This is also referred to as “open/closed principle”, taken from Bertrand Meyer’s quite well-known writings about object-oriented programming:

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

  • Open for extension means that an entity can be made to behave in different ways from the original requirements, and that new requirements can be added.
  • Closed for modification means that the code of such an entity must not be changed once it is declared completed.

If a system does not meet these criteria, it will become very difficult to maintain and evolve, quickly leading to obsolescence.

These criteria are even more important for a commercial software framework that is somehow opinionated and adopts lots of conventions. If it weren’t extensible, then it wouldn’t be used by developers who didn’t like the opinions or conventions adopted.

Because of that, ASP.NET MVC has been designed with extensibility as one of the main objectives; almost every step of the processing pipeline can be replaced with your own implementation. For example, if you don’t like the default convention of having actions as public methods of a class, whose name is the name of the controller plus “Controller”, you might swap it with your own IControllerFactory that implements the convention you think is most suitable for you or your team.

Chances are you’ve already extended ASP.NET MVC at some point or other. If you’ve used a view engine other than WebForm View Engine or Razor (such as Spark, for example), or if you’ve used a IoC container to create your controllers (such as Ninject or Unity), then congratulations! You’ve already used extensions, albeit other people’s.

Where Extensibility Points fit into the Processing Pipeline

At a very high level, the lifecycle of a request in ASP.NET MVC is:

  • The browser sends the GET or POST HTTP request;
  • The request is inspected and routed to the right controller and the right action;
  • The action collects or creates the data that needs to be returned to the browser;
  • This data is passed to the view that renders the response, which is sent back to the browser.

Hopefully you already knew this, but to give you a bit more context regarding where we can place all the various extensibility hooks, next we’re going to step through the processing pipeline in more detail, and with the help a flow diagram to visually identify it. I know that there’s a lot of ground to cover here, and it can appear quite dense if you’re not familiar with it, but take your time as you go through this material, and it should all be clear by the end.

The Routing part of the pipeline

The first macro area of the pipeline is the routing.

Note: The complete flow chart for these steps is available to download as a .PDF from the bottom of the article, or from here.

  1. Upon receiving the request, the Routing module tries to match the incoming URL with the routing table of the application. The matching is usually done by evaluating the routing URL pattern, or can be made more powerful by specifying Route constraints (IRouteConstraint), allowing you to apply more complex evaluation logic, and even talk to databases or webservices.

    1358-MVC1.png

  2. Once the Route has been selected, the corresponding IRouteHandler is called. The default one calls the MvcHandler, which starts the real processing inside ASP.NET MVC. Obviously, if you replace the default handler with something completely different, you also take over the processing of the request, and so all the steps that follow might not apply anymore.

    1358-MVC2.png

Creation of the Controller

  1. Based on the route parameters and the default naming conventions, the IControllerFactory creates an instance of the controller. If an IoC container has been configured, the controller factory retrieves the instance of the controller with the help of the IDependencyResolver.

    1358-MVC3.png

The Action execution

Once the controller has been instantiated, the specified action has to be executed, and this is handled by the IActionInvoker. As with the IRouteHandler, in theory you could completely replace this component with your own implementation, but then you will be developing against your own conventions. This is certainly not a problem, but it just means that the next steps will not apply:

  1. 1358-MVC4.png The first part of the process involves identifying the method to execute: in the simple scenario, the method which shares the same name as the action is chosen, but this behavior can be modified by applying an attribute of type ActionNameSelectorAttribute, which allows you to apply custom logic to this process. If more than one method is found, the correct one is chosen with the help of another kind of attribute applied to the methods: ActionMethodSelectorAttribute.

  2. If the method selected for execution has one or more parameters, they have to be filled in with the right values. The Model Binder (or rather, the IModelBinder interface) is the component responsible for this: it not only deserializes the HTTP request data into CLR objects, but also validates that the values are correct. This is done based on the validation rules (if the default ones are not enough you can write your own rules and validation logic) returned by the Model Validation Provider. Alternatively, another way to validate an object is to implement the IValidatableObject for cases where you need a validation rule that acts on the whole object, rather than just individual properties.

    1358-MVC5.png

  3. to the action. These filters are discovered via another component that can be customized and replaced: the IFilterProvider. In case you are using IoC, the filters are also instantiated using the IDependencyResolver. Out of the four types of filters available, two of them are executed before the actual action method: Authorization filters and part of the Action filters (specifically, OnActionExecuting). Filters are a very powerful way to implement orthogonal or cross-cutting behaviors that have to be shared among many actions or controllers. Filters can be applied per action, per controller, or even per application.
     1358-MVC6.png

  4. If the filters don’t stop the execution the action method is executed and the parameters are filled in with the values that were previously discovered by the Model Binder
    1358-img10.jpg.

  5. When the action method has completed its tasks, it returns a result of type ActionResult. This is executed to return the response back to the client that sent the original request. However, there might be other filters that can be applied before the response is returned: the OnActionExecuted and the OnResultExecuting.

    1358-MVC7.png

Finally, the View

There are many action results: one simply returns a text string, another returns a binary file or a Json formatted result. Yet the most important Action Result is the ViewResult, which renders and returns an HTML page to the browser.

  1. In ASP.NET MVC 3 there are two standard view engines: the WebForm view engine and the new Razor view engine, and their views are just normal .aspx pages (typically without a code-behind file) or ASP.NET Web Pages. However, more than one view engine can be registered, so the first step in the execution of the View Result involves selecting the appropriate engine to use and the view to render; which is all done via the IViewEngine interface of the view engine (which, despite name, takes care of finding the correct view).
     1358-MVC8.png

  2. Once the correct view engine and the view is chosen, it is instantiated, again with the help of the IDependencyResolver, and the model is then passed to the view.

    1358-MVC9.png

    The lifetime of a view then depends a lot on what it does; in the simplest case it could just render a bit of text, even without the need of HTML helpers. Alternatively, in a more complex scenario, it might render a form with JavaScript validation. In the latter, more complex case, we actually encounter two new extensibility points: the templated helper… er… templates, and the Model Metadata Provider. These two components work together to help you write less code, automatically creating the form based on the properties of the model object that you have to edit, as well as the metadata attached to it. While the default provider looks at attributes on the model class, if you want to store the metadata somewhere else you can write your own provider that provides metadata based on your own conventions.

  3. 1358-MVC10.pngAt this stage, the Model Validation Provider (which we encountered during the action execution) comes onto the stage again. Once the information from the model and the validation rules has been retrieved, client-side validation scripts are automatically added to the page. This means that verifying the length of a string, the value of a number and many other simple requirements can happen without you having to writing a single line of code. Just like the server-side validation that happens during the model binding stage, you can also add your own validation rules on the client side within their JavaScript code.

Another type of validation that happens on the client is remote validation; this is needed when the validation rules require access to resources on the server. For example, if you have a form in which you need to check whether a username has already been taken, the validator needs to call back to the server to determine whether the field is valid.

Now, when writing the code for the view, you will use three libraries:

  • HtmlHelper
  • AjaxHelper
  • UrlHelper

They contain helper methods to write input fields, create links based on the routes, AJAX-enabled forms, links and more. Moreover, extending them is very easy, and writing new methods for these libraries, as opposed to writing the code directly inline in the view, is also considered an ASP.NET MVC programming best practice.

Types of extensibility points

Now that we’ve gone over the process pipeline in more detail, you hopefully have a better idea of what’s happening under the hood, and you should also be starting to get a feel for the number of opportunities you have to extend your ASP.NET MVC application. However, as you might have already noticed, not all extensibility points need to be used with the same frequency:

Essentials

Some customizations are integral parts of the development of a web application with ASP.NET MVC, and thus are used in every project. The most common extensibility points in this category are:

  • Custom templates for the templated helpers
  • Server-side validation rules
  • Client-side validation rules
  • HTML Helpers

Productivity enhancers

Other extensibility points are not always needed, but if used can help make development more productive and less repetitive:

  • A base controller to enforce your own conventions
  • AJAX/URL Helpers
  • Action/Result Filters

Case-Specific

Next up, there are other extensibility points that you only need to use if you need to solve a specific problem:

  • Route Constraint
  • Route Handler
  • Action Result
  • Model Binders
  • ActionMethodSelector

Core Extensions

Finally, there are the extensibility points that change a big part of ASP.NET MVC, and which you are not very likely to implement. These might even remove some of the other extensibility points and add their own. Nevertheless, there is a very high chance that you used a custom component for one of these points that was developed by someone else:

  • Controller Factory
  • Action Invoker
  • View Engine
  • Model Validation Provider
  • Model Metadata Provider

How these Articles will be structured

During the course of these articles, I’m going to explain how to implement your extensions to ASP.NET MVC, starting with the ones at the beginning of the pipeline (routing extensions) and finishing with the view extensions points. I’ll focus mainly on the first two categories (“Must Use” and “Productivity Enhancers”), but I’ll also briefly touch upon the more rarely implemented ones, where there are useful modifications to be made.

For each point explained, a common structure will be followed:

  • why your own custom implementation might be needed
  • area of extensibility (routing, controller, views, etc)
  • type of extensibility (must use, only when necessary, etc)
  • the interfaces that have to be implemented
  • what the default implementation (if it exists) does
  • sample of already available implementations in the community (if any)
  • sample implementation

Thus far we’ve seen an overview of the entire ASP.NET MVC processing pipeline, and should now have a feel for the huge number of opportunities available to extend and customize how the framework behaves. We’ve also seen that not all extensibility hooks are created equal, and there are some which are more useful than others. Now that we’re all on the same page, the next article will cover how the routing process can be extended.

The free wallchart, ‘ASP.NET MVC Pipeline’, that goes with this article is available as a PDF file from the speechbubble at the head of the article or from here. It is best printed on an A3 printer.

Keep up to date with Simple-Talk

For more articles like this delivered fortnightly, sign up to the Simple-Talk newsletter

Downloads

This post has been viewed 60902 times – thanks for reading.

Tags: , , , , , , , , ,

  • Rate
    [Total: 165    Average: 4.6/5]
  • Share

Simone Chiaretta is a software developer and architect, coding on the .NET platform for business and for fun since 2001. He has worked for the web agency Esperia in Milan before moving to New Zealand as Chief Software Architect for Calcium Software. Now back in Italy, he works for Avanade as Senior Solution Developer. He also works on the SubText open-source blogging platform. When he’s not writing code, blog posts or taking part in the worldwide .NET community, Simone enjoys free-climbing, mountain climbing and ice climbing.

View all articles by Simone Chiaretta

  • abellix78

    Great article
    Nice and complete. I’ll keep this as a reference!

  • mechInferno

    Great MVC breakdown.
    This is just what I’ve been looking for. I’ve dabbled a bit in MVC development but I’m still fairly new to it.

    I’m looking forward to the rest of these articles.

    Thanks,

  • Anonymous

    Nice article
    Looking forward to upcoming articles on subject.

  • ymhee_bcex

    Excellent start
    Simone,

    This is a very challenging topic. You say (tongue in cheek, I am sure) that “if you are not extending, you are not doing it right”, but one needs to be darn sure that he is indeed *extending* and not replicating something that already exists and he just doesn’t know about it! So, most books are naturally concentrating on teaching what already exists.

    Moreover, the decision to extend shouldn’t be taken lightly – I would strongly discourage to extend just in order to implement the convention you think is most suitable for you or your team. Team members change; somebody else will maintain this code – and it’s better to stay with what’s accepted universally.

    BUT – with these disclaimers – the subject of extending MVC clearly hasn’t been covered enough! There was something written about Validation Provider, Model Binder, Action Filters – but not enough AND not well enough!

    Good luck!

  • Anonymous

    need
    I feel, You need to give few examples as well, otherwise there are plenty of such articles on the web.

  • simonech

    This is just the introduction
    Dear ymhee_bcex,
    I agree, extending should not be done because you don’t know what the standard behavior is.
    But I think that ASP.NET MVC is a fairly not-opinionated framework on purpose: to allow developers to build their own “opinions” on top of it.
    Till v2 you had to build an extension if you wanted to use IoC/DI to create controllers.
    With conventions I mean piece of code you find yourself writing over and over again. Sure you can create a “standard” method, but most of the times, in ASP.NET MVC, you can find a way to put it into the pipeline, and hide it away. I agree sometimes things can become too “automagical”, but if they make development faster and less repetive, I go for them.
    Developers come and go… but 99% of the teams I’ve been at already had helper libraries or even internal development libraries, so a new developer always have to tune into the practices of a team.

    Anyway, there will be other 9 articles covering why extending, how to extend, with real worlds examples. I’ve blogged on my own blog the (tentative) list of articles:
    10 articles on ASP.NET MVC Extensibility to land on Simple-Talk in the next months

    Hope this answered your comment.
    Simone

  • Anonymous

    I want more! =)
    awesome article. What are your references? how did you obtain all the knowledge required to write your article? Even books that are prefixed with “Pro” and “Advanced” are not nearly as advanced as this article.

    Thanks

    • simonech

      Re: I want more! =)
      Dear Anonymous,
      the reference, most of the time for advanced topics, is code. ASP.NET MVC is opensource, so all code is available in its origianal form.
      And besides, what would have been the added value of writing an article with content coming from other books? πŸ™‚

  • Anonymous

    Thanks!!!
    Very nice post, a must read. Thanks a lot.

  • Anonymous

    Great Article
    This article is great reading and put together well. Thanks!

  • Make up ideas

    Bare minerals store
    To facilitate was a impressive place of duty. It is inspiring on behalf of all. Credit on behalf of sharing to facilitate slice.

  • Holger

    Great post
    Looking forward to the next articles.

  • Tom

    Looking forward to reading the rest
    Very clear article, looking forward to reading more. How long till you publish the next one?

    • simonech

      Re: Looking forward to reading the rest
      Next article is already in the works.

  • Anonymous

    Cool article
    Thank you very much for such useful article

  • Anonymous

    Waiting for next article.
    Thanks for this post.

  • Anonymous

    Great and want more
    Perfect !! waiting for more in this topic.

  • jchannon

    Plugin Architecture
    Great article and proposed ones. I was hoping that you might do an example of extensibility by illustrating how to do a plugin architecture ie. Have a main MVC app which will accept compiled dll’s in the bin folder

  • jchannon

    Plugin Architecture
    The compiled DLL would have controllers, models and views but would be a plugin.

    Would you be able to include this in your articles?

  • ymhee_bcex

    I am sure the next chapter is coming really soon now
    In the meantime only comment spam is piling up πŸ™

  • maxtyrann

    Very good article
    This is probably the best article if you want to know how ASP.NET MVC 3 works behind the scenes and that diagram is very very useful to summarize the information here. Thanks and congratulations ^^

    I hope you can publish the rest of 10, a 6 month absence is a little discouraging, it kinda reminds me Andrew Siemer’s article series at Dotnetslackers πŸ™

  • cherkaouimouad

    Good Article.
    thanks for the good informations and explanations ,
    I was also reading the article about extending the routing system, but cant found the next articles about extending Mvc 4 or 5 ,
    finally thanks for the Article ;

  • Manvir

    Nice article
    Thanks for this article. It will be great if we can find some reference to next article within this series.