Examining Application Startup in ASP.NET 5
ASP.NET 5 differs from previous versions of ASP.NET in many ways. Gone is the default ASP.NET event life cycle, and along with it, the global.asax file (which itself was an evolved version of global.asa from the ASP days). Also gone is the XML-based web.config file, in most cases, though you may still find it making appearances in your wwwroot folder in some cases.
Note: The official ASP.NET 5 documentation is still under development. It’s being developed using GitHub and open source, and Falafel Software is one of the official contributors. Some of the links in this article are still under construction, but you should see them fill out by the time ASP.NET 5 is officially released. A good place to get started with this topic is the Application Startup article that was just published.
ASP.NET 5’s startup system is heavily influenced by prior work in the OWIN space, including project katana. In this new paradigm, the web host environment (not necessarily IIS) will search the application for a starting point, and typically this will be a class called Startup located in the root of the application. At a minimum, this class needs to have a method called Configure(), which the hosting environment will call to configure the application’s request pipeline. If you’re familiar with ASP.NET’s HTTP handlers and modules, you can think of the request delegates that are specified within theConfigure() method as being similar to a combination of these two concepts. Collectively, such request delegates are referred to as middleware.
Having complete and granular access to the HTTP request delegate pipeline allows applications to be constructed with just the features and components they require, and nothing more. It’s extremely lean, composable, and more secure and higher performance by default because only those features that are required are included. This reduces the overall application surface area exposed to attackers, and reduces the work that must be done on each request to the bare minimum.
The request delegate pipeline is constructed as a series of delegates, which can be written simply as lambda expressions or encapsulated within their own classes. Each delegate can perform some work, call the next delegate, and then do some more work once that call completes. Thus, it’s possible to wrap later delegates within earlier ones, which is important for scenarios like authentication and error handling. At any given point within the pipeline, a delegate can choose not to call the next delegate, even if one is defined, allowing the pipeline to be short-circuited. Thus, the order in which delegates are wired up in the pipeline is very important.
Consider this sample method from the default web site template (in VS2015 RC):
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { if (env.IsEnvironment("Development")) { app.UseBrowserLink(); app.UseErrorPage(ErrorPageOptions.ShowAll); app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll); } else { // Add Error handling middleware which catches all application specific errors and // sends the request to the following path or controller action. app.UseErrorHandler("/Home/Error"); } // Add static files to the request pipeline. app.UseStaticFiles(); // Add cookie-based authentication to the request pipeline. app.UseIdentity(); // Add authentication middleware to the request pipeline. You can configure options such as Id and Secret in the ConfigureServices method. // For more information see http://go.microsoft.com/fwlink/?LinkID=532715 // app.UseFacebookAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); }); }
In this example, the pipeline is configured slightly differently in a development environment vs. in production. In development, the application wires up BrowserLink (for use with Visual Studio) as well as helpful error pages that should not be deployed to production. In production, a simple error handler page is configured. Next, the application is configured to support static files, and then to use ASP.NET Identity for authentication. Note that since authentication is configured after static files, it will not protect static files (nor will static files incur overhead from checking authentication). Finally, ASP.NET MVC is configured, along with a default route. In each case, a simple UseWhatever() method is used to wire up the pipeline methods, but under the covers these methods are adding request delegates to the application’s pipeline. Although the Configure() method is called when the application starts up, the request delegates that are wired up here are not – they are called on every individual HTTP request that is made to the application.
In addition to Configure, you can optionally specify a method called ConfigureServices, which will configure the default services container (IoC container) for ASP.NET. This allows for dependency injection, which helps ensure your application remains loosely coupled from its underlying implementation. Your controllers, services, and other application classes should try to follow the SOLID principles, as well as the Explicit Dependencies Principle, which will help to ensure you don’t end up with a brittle, tightly coupled implementation. These principles have been followed by ASP.NET itself in this version. Note that your Startup class doesn’t depend on any particular base class, nor does it refer to any particular implementation of a web server. Even the parameters it can have injected into it are all interfaces, not concrete types, allowing multiple web servers to be supported, even across multiple platforms.
You can see how ASP.NET 5’s hosting implementation loads an individual application by examining the Microsoft.AspNet.Hosting package – it’s open source on GitHub. Specifically, you can see how an application is created and its services registered by examining the HostingEngine class, which the Configure class inside its BuildApplication method. The application’s Startup class has its methods mapped to a StartupMethods class by StartupLoader, which allows HostingEngine to call these methods without having any knowledge of your application’s actual Startup class’s type.
Take a look at how these two classes, StartupLoader and HostingEngine, work to start and configure your application. This is fundamental to understanding how ASP.NET 5 applications work at the most basic level. With ASP.NET 5 being developed completely as open source on GitHub, we have much greater ability to examine and understand the underlying design decisions and plumbing that underpins the applications we build on this foundation. Use this to your advantage and make sure you can follow what the two classes above are doing when your application is launched. Do not expect that your understanding of ASP.NET from prior versions will serve you well – this version is a huge change from ASP.NET v1-v4. It’s not quite as big a change as from classic ASP to ASP+/ASP.NET, but it’s close.