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.