May 2017

Volume 32 Number 5

[Cutting Edge]

ASP.NET Core for ASP.NET Developers

By Dino Esposito | May 2017

Dino EspositoMost of the buzz aboutASP.NET Core is centered on the multi-­platform experience it enables. While this is a huge achievement, it’s not necessarily a plus if you’re a regular ASP.NET user with a large code base of .NET 4.x code and no plans to leave the familiar IIS and Windows environment. In this case, what’s the value proposition of ASP.NET Core for such a regular ASP.NET developer?

At first the new platform might look completely different, as if someone sneakily moved your cheese elsewhere overnight. ASP.NET Core is new, rebuilt from the ground up, according to more modern practices. This might or might not increase your programming power and your ability to address customers’ concerns. Nobody can realistically answer that question on your behalf. This column attempts to clear the ground from any hype, benchmarks, and technology focus and go straight to the substance of things. If you’re OK with the current platform, which aspects of ASP.NET Core can capture your attention?

Common Practices Engineered in the Framework

When the ASP.NET team members designed the original ASP.NET framework, they took most of the best practices of Active Server Pages and engineered them into a new framework. In doing so, they also introduced a lot of new stuff, such as compiled and managed code, automatic postbacks, and server controls. ASP.NET Core follows the same evolutionary pattern.

Common development practices, such as initial loading of configuration data, dependency injection, NuGet packages, claims-based authentication and Razor improvements, are native features of the new framework. The new framework also has a different startup procedure, a much more modular request-response middleware and even a slightly more flexible infrastructure for defining controllers and views. ASP.NET Core is also a cross-platform framework and lets you develop applications and host them on Windows, as well as macOS and Linux. In a way, ASP.NET Core forces you to write better code where a few additional levels of separation of concerns are forced by default. It’s not anything that you can’t already achieve with discipline, though.

For any form of greenfield development, ASP.NET Core is an excellent choice. Yet, being a brand-new framework, it has some unavoidable initial costs: Everyone on the team must become proficient with it. In addition, everyone must be, or become, proficient with the Model-View-Controller (MVC) application model. Not everything you can label as greenfield development is brand new. Reusing chunks of existing code, or at least existing skills (that is, data access or security skills), is desirable. How much of that is realistically possible? To address this point, ASP.NET Core comes in two flavors.

Flavors of ASP.NET Core

In Figure 1, you see the Visual Studio 2015 dialog box to create a new project. (It’s essentially the same in Visual Studio 2017.)

Creating a New ASP.NET Core Project in Visual Studio
Figure 1 Creating a New ASP.NET Core Project in Visual Studio

The first template will create a classic, non-Core project. The other two templates can create an ASP.NET Core project for a different .NET Framework. It’s the first crossroad you face in your journey through the ASP.NET Core unexplored territory.

Opting for the full .NET Framework gives you access to any existing .NET class libraries, but limits hosting to only Windows and IIS. Figure 2 summarizes the differences.

Figure 2 Fundamental Differences Between Flavors of ASP.NET Core

Framework Facts
.NET Framework

ASP.NET MVC only; no WebForms

New runtime environment and programming API

Any libraries targeting the selected version of the .NET Framework 

Only IIS hosting

.NET Core

ASP.NET MVC only; no WebForms

New runtime environment and programming API

Only .NET Core libraries

Cross-platform hosting

Regardless of the choice of the .NET Framework, going with ASP.NET Core exposes your code to a new runtime environment that unifies the MVC runtime based on system.web with the Web API runtime inspired by the OWIN principles.

Breaking Up with IIS

In recent years, the Web API framework attempted to address the high demand of thin servers capable of exposing a RESTful interface to any HTTP-enabled clients. Web API decoupled the application model from the Web server and led to the OWIN specification, namely a set of rules for Web server and applications to interoperate. Web API, however, needs a host and when hosted in the context of an ASP.NET application, it just adds another runtime environment to the memory footprint. This happens at the same time in which the industry is moving toward super-simple Web. A super-simple, minimal Web server is an HTTP endpoint to get content as quickly as possible, just a thin HTTP layer around some business logic. All that a super-simple server needs to do is process the request as appropriate and return a response with no overhead except for the business logic.

Although customizable to some extent, the current ASP.NET runtime environment wasn’t designed to address similar scenarios. Separating the ASP.NET environment from the hosting environment is the key change you find in ASP.NET Core and the reason for many of the subsequent application-level changes.

Startup of an Application

After creating a new project, the first thing you notice is the lack of a global.asax file and the presence of a program.cs file. As shocking as it might be, an ASP.NET Core application is a plain console application launched by the dotnet driver tool (bit.ly/2mLyHxe).

The dotnet tool is the key to multi-platform support. Once the command-line tool (and the .NET Core framework) is available for a new platform, hosting reduces to connecting the Web server to the tool. Under IIS, publishing is accomplished through an ad hoc module, the .NET Core Windows Server Hosting package (bit.ly/2i9cF4d). Under Apache, on an Ubuntu server, it’s achieved through a configuration file. An example can be found at bit.ly/2lSd0aF.

The actual Web server works as a reverse proxy and communicates with the console application over a configured port. The console application is built around another, simpler, Web server that receives requests and triggers the internal application pipeline to have them processed. The following code does the job:

var host = new WebHostBuilder()
  .UseKestrel()
  .UseContentRoot(Directory.GetCurrentDirectory())
  .UseIISIntegration()
  .UseStartup<Startup>()
  .Build();
Host.Run();

Kestrel is the name of the ASP.NET Web server that receives inbound requests and processes them through the pipeline. The IIS Integration module call in the code snippet is only required if you host under IIS.

Having a reverse proxy around the ASP.NET Core application is recommended primarily for security reasons as the Kestrel internal Web server doesn’t include (at the moment) filters to prevent things such as distributed denial of service (DDoS) attacks. From a purely functional point of view, you don’t strictly need a reverse proxy enabled.

As mentioned, there’s no longer a global.asax file in an ASP.NET Core application and the role of the web.config file is wildly dimin­ished. Actually, it only serves the purpose of enabling IIS to do some work on behalf of the application, such as serving some static error pages. Key things such as configuring error handling, logging, authentication and storing global configuration data are done through a new API orchestrated from the startup class.

The Startup Class

The startup class contains at least a couple of methods that the host will call during the initialization phase:

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  public void Configure(IApplicationBuilder app)
  {
    app.Run(async (context) =>
    {
      await context.Response.WriteAsync(DateTime.Now)
    });
  }
}

Through the ConfigureServices method you declare the system services the application will use. Technically, the method is optional, but I’d say that one is necessary in any realistic scenarios. To a traditional ASP.NET developer, it might be shocking to find that even the use of the MVC application model must be explicitly declared and enabled. However, this fact gives the measure of how seriously modularization is taken in ASP.NET Core:

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();
}

In the method Configure, you configure any previously requested services. For example, if you requested the ASP.NET MVC service, then in Configure you can specify the list of supported routes. Note that you also need an explicit call to enable the internal Web server to serve static files, including common files such as jQuery and Bootstrap:

public void Configure(IServiceCollection services)
{
  app.UseStaticFiles();
  app.UseMvcWithDefaultRoute();
  ...
}

The startup class is also the place where you configure the middleware of the application. Middleware is a new term that has a significant conceptual overlap with the HTTP modules of current ASP.NET. In ASP.NET Core, the middleware works as shown in Figure 3.

The ASP.NET Core Middleware
Figure 3 The ASP.NET Core Middleware

You can register chunks of code that have a chance to pre- and post-process any incoming request, meaning that each middleware can register code that runs before or after the terminating middleware—the method Run in the Configure method of the startup class. The overall model is similar to the old ISAPI model of IIS. Here’s some sample middleware:

app.Use(async (httpContext, next) =>
{
  // Pre-process the request
  // Yield to the next middleware
  await next();
  // Post-process the request   
});

A middleware is a function that takes an HttpContext object and returns a Task. The list of middleware components ends with the Run method. Compared to the current ASP.NET pipeline, the ASP.NET Core pipeline is bidirectional and fully customizable. Moreover, it’s empty by default.

Super-Simple Web Services

It’s understood that without a Run method in the pipeline, no requests will ever produce a response. At the same time, a Run method is all you need to produce a response. This shows how short the pipeline can be in an ASP.NET Core application. In ASP.NET WebForms and MVC, many things happen before your own code runs for each request. Resolving a controller method, for example, is quite a long procedure that involves an entire subsystem centered on the action invoker. The Run method, instead, is a direct call that immediately follows the receipt of the request.

Suppose you want to create a file server that owns a list of image files (say, flags) and returns a properly sized image based on some input parameters or device properties. In today’s ASP.NET, the fastest option is probably writing an ad hoc HTTP handler—a class created by implementing the IHttpHandler interface mapped to a fixed URL route. An HTTP handler is faster than an ASPX endpoint and MVC controller action because of the slimmer pipeline. It also has a smaller footprint than a Web API endpoint because it doesn’t require a second OWIN pipeline on top of the basic ASP.NET pipeline. (This isn’t the case when a Web API solution is hosted outside IIS.)

In ASP.NET Core, creating an effective file server is easier than ever and more effective than ever. All you do is craft the additional logic (that is, resize/retrieval) and bind it to the Run method of the ConfigureServices method:

public void Configure(IApplicationBuilder app)
{
  app.Run(async (context) =>
  {
    var code = context.Request.Query["c"];
    var size = context.Request.Query["s"];
    var file = FindAndResizeFlag(code, file);
    await context.Response.SendFileAsync(file);
  });
}

In the example, I assume to have some custom logic to find a server file name that matches provided parameters, and then I serve it back to the caller via the Response object. No other code, either visible or invisible, is required. Frankly, it couldn’t really be easier than this.

Whatever your gut feeling is about ASP.NET Core, whether you’re skeptical or enthusiastic about it, ASP.NET Core provides a unique capability you won’t find on any ASP.NET platform: creating super-simple minimal Web services. At the same time, the same infrastructure that lets you build super-simple Web services is the best guarantee that any requests can be served with the minimum overhead that’s legitimately possible.

Wrapping Up

To be a productive ASP.NET Core developer, you only need to be familiar with an application model more modern than WebForms: this means ASP.NET MVC or the single-page application model. In spite of the appearances, most of the breaking changes of ASP.NET Core are in the runtime environment. Understanding the hosting model and the middleware is enough to make sense of the new platform. In the end, it’s still about creating controllers and rendering Razor views. A few common things like authentication, logging and configuration will need a different API, but learning that only takes a while. As I see it, the challenge is finding what’s in it for you.


Dino Esposito is the author of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2014) and “Modern Web Applications with ASP.NET” (Microsoft Press, 2016). A technical evangelist for the .NET and Android platforms at JetBrains, and frequent speaker at industry events worldwide, Esposito shares his vision of software at software2cents.wordpress.com and on Twitter: @despos.

Thanks to the following Microsoft technical expert for reviewing this article: James McCaffrey


Discuss this article in the MSDN Magazine forum