January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

ASP.NET Core - Startup, Services and Middleware

Recently during one of my training programs someone asked - Can we change the name of the startup class in ASP.NET Core? What's the flow of an ASP.NET core request? This article attempts to answer these beginner questions in detail. It discusses the ASP.NET Core terminology such as application startup, services and middleware. It also briefly tells you how a custom services and middleware can be created and used in ASP.NET Core applications.

Under ASP.NET Core a request passes through a series of stages as discussed in the following sections. Let's test what we learnt so far by creating a new ASP.NET Core application using Web Application project template.

Once you create a new ASP.NET Core web application set breakpoints as follows. These breakpoints will help you understand the flow of execution clearly.

  • Open Program.cs file and set a breakpoint at the Main() method
  • Open Startup.cs file and set breakpoints in the constructor, ConfigureServices() method and Configure() method respectively.
  • Open HomeController.cs file from the Controllers folder and set a breakpoint in the Index() action

Now run the application by pressing F5.

The Main() method

When you run the application your execution halts at the Main() method from the Program.cs file. If you are not familiar with ASP.NET Core you might be surprised to know that just like console applications or Windows Forms applications, ASP.NET Core application also has the entry point - Main(). You will find the following code in the Main() method:

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

The Main() method is quite similar to that of a console application. Inside, it has a piece of code that configures the web server to be used and also the startup class. Did you notice how the Startup class is specified? You might have already guessed that since it is specified here in the Main() its name could be anything. For example, later you are going to change its name from Startup to MyStartup. That works just fine.

The Run() method of the WebHostBuilder runs the web application and blocks the calling thread till the host shuts down (remember that Console.ReadLine() we put to block the thread? It's something similar.)

Ok. So, the starting point for an ASP.NET Core application is Main().

Startup constructor

Press F5 to continue the application run. The debugger will now pause at the Startup class constructor. That means the WebHostBuilder has now loaded the Startup class. The constructor is usually a place to read the application configuration or do such initialization tasks. The following code shows one such usage of the Startup constructor.

public class Startup
{
    public IConfigurationRoot Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder();
        builder.SetBasePath(env.ContentRootPath);
        builder.AddJsonFile("appsettings.json", 
             optional: true, reloadOnChange: true);
        Configuration = builder.Build();
    }
    ....
}

We won't go into the details of the constructor code since that's not the topic of our discussion. The point is - the host has now loaded the application startup class (Startup in this case).

ConfigureServices() method

Another hit to F5 will land you in the ConfigureServices() method. The ConsigureServices() method is a place where you add services required by your application. Let's understand this in bit more details.

ASP.NET Core uses an inbuilt dependency injection framework. If you wish to use DI with your services you need to register them with the underlying DI container here. A service is basically a component that does some work for you. For example, you can register Entity Framework Core as a service here. The following code shows a sample ConfigureServices().

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

As you can see the above code registers two services with the framework - Entity Framework Core and MVC. Of course, you can also create your own services as illustrated later in this article.

Configure() method

The next F5 will take you to the Configure() method. The Configure() method is used to configure the middleware used by your application. What's a middleware? Basically, middleware is a component that you plugin to the request pipeline. Remember the HttpModules we used to create? Middleware is a similar concept and comes from OWIN specifications. So, your request comes to a web server and from thereon it can be made to pass through a series of middleware components, each performing some specific task. Consider the following code:

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}
                /{action=Index}/{id?}");
    });
}

Here the application indicates that static files middleware and MVC middleware is being used by your application. That means MVC is just a middleware from the point of view of the request pipeline. There could be some other middlweare, say security middleware, logging middleware and so on. The sequence in which the middleware components get invoked is determined by the sequence in which they were added.

Later in this article you will create your own middleware and plug it in the request pipeline.

The Controller

The final hit to F5 will take you to the Index() action of the HomeController. This is where your application code gets executed.

So far so good. Before we move ahead let's put our learning into a simplified pictorial form as shown below:

Once the startup configuration is loaded the chain of middleware is invoked as shown below:

 

Note that the startup constructor, ConfigureServices() and Configure() is called during the first request. For the subsequent requests the configuration of services and middleware need not be loaded again because the host has already loaded it. So, the subsequent requests are taken directly passed through the middleware chain.

Changing startup class name

By default the startup class is named Startup. However, you can easily change its name if you so wish. Let's see how.

Open the Startup.cs file and change the startup class name from Startup to MyStartup. Also, make sure to adjust the constructor accordingly.

public class MyStartup
{
    public MyStartup(IHostingEnvironment env)
    {
       ....
    }
}        

Then open Program.cs file and change the UseStartup() call as follows:

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<MyStartup>()
        .Build();

    host.Run();
}

Run the application to confirm that it still works as expected.

Creating a custom service

Creating a custom service is quite straightforward. A service is type that you register with the framework so that you can use DI with those types. Consider the following class:

public interface IMyService
{
    string GetTimeStamp();
}

public class MyService:IMyService
{
    public string GetTimeStamp()
    {
        return DateTime.Now.ToString();
    }
}

The above code defines IMyService interface and MyService class that implements that interface. The MyService class contains GetTimeStamp() method that simply returns current date-time value as a string.

How to register MyService with the DI framework? You have two options.

First one - you can register the individual services types in the ConfigureServices() itself. This is shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFrameworkSqlServer();
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
    ....
}

Here we registered MyService with the DI framework with a lifetime mode of Scoped. We won't go into the details of DI features of ASP.NET Core in this article. You may read this article to know more.

Second way involves creating an extension method and calling it from the ConfigureServices(). If you wish to register many types or there is some complex logic involved during the registration then this approach will be good. Consider the following code that creates such an extension method:

public static class MyServiceExtensions
{
    public static void AddMyService(
           this IServiceCollection services)
    {
        services.AddScoped<IMyService,MyService>();
    }
}

The above code adds an extension method - AddMyService() - to the IServiceCollection. The extension code contains call to AddScoped() that registers MyService with the DI feamework. Once created you can call this method from ConfigureServices() like this:

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

Now, your code follows the pattern as that of EF Core and MVC.

Creating a custom middleware

Writing your own middleware is slightly complex that creating a service. That's because you need to ensure that your middleware will plugin to the request pipeline without any side effects. Although we won't go into detailed discussion of writing custom middleware here, we will create a simple middleware just to make this concept clear.

Consider the following class the represents a middleware component.

public class MyMiddleware
{
    private RequestDelegate nextMiddleware;

    public MyMiddleware(RequestDelegate next)
    {
        this.nextMiddleware = next;
    }

    public async Task Invoke(HttpContext context)
    {
        context.Items.Add("middlewareKey", 
                     "Inside MyMiddleware....");
        await this.nextMiddleware.Invoke(context);
    }
}

The MyMiddleware class represents a middleware component. It consists of a constructor and the Invoke() method. The constructor receives a RequestDelegate object - references to the next middleware in the pipeline. The RequestDelegate object is stored in nextMiddleware variable. The Invoke() method is an asynchronous method and does two things. It adds an item to the HttpContext's Items collection and then invokes the next middleware in the pipeline. The former represents the processing your middleware is supposed to do such as, say, authentication or logging. Here for the sake of simplicity we only add an item to the Items collection.

How do you use this middleware? Again two approaches are available.

First - you call AddMiddleware() method of the IApplicationBuilder in the Configure() as shown below:

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<MyMiddleware>();
    app.UseStaticFiles();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

As you can see you indicate to the framework that you wish to add MyMiddleware at the beginning.

Second - you can resort to the extension method technique similar to what you did with the services. This is shown below:

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware
              (this IApplicationBuilder app)
    {
        return app.UseMiddleware<MyMiddleware>();
    }
}

Here you added an extension method - UseMyMiddleware - to the IApplicationBuilder. Inside, you simply call the UseMiddleware() method of the IApplicationBuilder. Once created you can use the UseMyMiddleware() extension like this:

public void Configure(IApplicationBuilder app)
{
    app.UseMyMiddleware();
    app.UseStaticFiles();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}
        /{action=Index}/{id?}");
    });
}

Now the Configure() method calls the UseMyMiddleware() to add your custom middleware to the request pipeline.

You can confirm from the Index() action whether the HttpContext contains the item added  by the middleware:

public IActionResult Index()
{
    ViewBag.TimeStamp = service.GetTimeStamp();
    ViewBag.MiddlewareKey = 
          HttpContext.Items["middlewareKey"];
    return View();
}

A sample run of  the application causes this output to be displayed.

That's it for now! I hope you must have got some idea about ASP.NET Core startup, services and middleware.


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 01 August 2016


Tags : ASP.NET ASP.NET Core MVC C# Visual Studio