ASP.Net core middleware is a component or piece of code executes in the request/response pipeline which decides whether to pass the request to the next component in the pipeline or not. Middleware does some kind of work before deciding to pass the request to the next component in the pipeline.
Let me explain with an example, say I want to log all the incoming requests first before it hits to any other component in the application. Also, I want to read a custom header value from the request and make it available throughout the application. Here we can create two custom middleware, the first one will write the log and the second one will read the value from the header and inject it to all needed components. This is what we are going to build today.
Before start building let me show a diagram which shows the execution of ASP.Net core middleware,

As you can see in the diagram above request pipeline consists of a sequence of middleware’s, called one after the other. Each middleware can perform operations before and after it delegates the request/response to the next middleware.
ASP.NET Core has many built-in middlewares (Example:- Authentication, MVC, Routing, etc.). But in this article, we are focusing on how to build middleware on our own and plug it with ASP.Net core request/response pipeline.
Let us start building custom middleware for below two scenarios,
- Logging Middleware
- Middleware to read request header
Rules to build custom middleware
There are certain rules to follow for writing a middleware. Middleware is generally encapsulated in a class and exposed with an extension method. The middleware class must include:
- A public constructor with a parameter of type RequestDelegate.
- A public method named Invoke or InvokeAsync. This method must return a Task and accept the first parameter of type HttpContext. Additional parameters for the Invoke/InvokeAsync are populated by dependency injection (DI).
No worries If you didn’t understand the rules. We will see each one of these in the example we are going to build. Enough of theory, let’s build our first custom middleware
Custom Logging Middleware
Here we will build a middleware where all requests should hit first in the pipeline which logs the request body. Let’s name the class “LoggerMiddleware”, which looks like below,
public class LoggerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public LoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<LoggerMiddleware>();
}
public async Task InvokeAsync(HttpContext httpContext)
{
//Read body from the request and log it
using (var reader = new StreamReader(httpContext.Request.Body))
{
var requestBody = reader.ReadToEnd();
//As this is a middleware below line will make sure it will log each and every request body
_logger.LogInformation(requestBody);
}
//Move to next delegate/middleware in the pipleline
await _next.Invoke(httpContext);
}
}
Now go back to the rule which we have mentioned before to build a custom middleware. We have exactly followed those rules. We have a public constructor with a parameter of type “RequestDelegate” in the LoggerMiddleware class. Also, we have a public method named InvokeAsync which returns a Task and accepts the first parameter of type HttpContext.
To enable logging we have injected ILoggerFactory, which is a .Net core framework service. we don’t need to worry about injecting ILoggerFactory as .Net core will take care of injecting framework services (Example of framework services IApplicationBuilder, IHostingEnvironment, ILoggerFactory, etc).
The next step is to create an extension method to expose the middleware. Middleware extension method exposes the middleware through IApplicationBuilder,
public static class LoggerMiddlewareExtension
{
public static IApplicationBuilder UseLogger(this IApplicationBuilder applicationBuilder)
{
return applicationBuilder.UseMiddleware<LoggerMiddleware>();
}
}
Here we have extended IApplicationBuilder to use the new middleware we have build, LoggerMiddleware. Now we are all set with the middleware and the only thing left is to use it.
To use any .Net core middleware we have to invoke it in the Configure method of Startup.cs.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Adding logger as the first component to execute in the pipeline
app.UseLogger();
app.UseClientConfiguration();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
By adding the line app.UseLogger() our application is ready to use the middleware we build. We have added it as the first component in the pipeline to make sure it will always log whenever a request hits the application.
Custom Client Configuration Middleware
Let’s build our second custom middleware. As an example scenario assumes in every API request client will pass client name in the header and we need to use that client name in multiple parts of the application along with the date and time client invoked the API. We are going to build a middleware for this example scenario where we read the header from the client and make it available in other places of the application. First, we will create an interface named “IClientConfiguration” with properties ClientName and InvokedDateTime. In the middleware, we set both properties. Now where ever we injected IConfiguration will have those values we set in the middleware.
Before start building this middleware I need to discuss a bit about middleware lifetime. Middleware is constructed once per application lifetime. Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren’t shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method’s signature. The Invoke method can accept additional parameters. Because of this lifetime rule, we will inject “IClientConfiguration” as a parameter to InvokeAsync method. If you couldn’t follow what I just said no worries you will get it after we built it. So let’s build it straightway. First things first, let’s add the interface “IClientConfiguration”,
public interface IClientConfiguration
{
string ClientName { get; set; }
DateTime InvokedDateTime { get; set; }
}
Next, a class which Implements IClientConfiguration,
public class ClientConfiguration : IClientConfiguration
{
public string ClientName { get; set; }
public DateTime InvokedDateTime { get; set; }
}
And I have resolved the dependency of IClientConfiguration in the ConfigureServices method of Startup.cs,
services.AddScoped<IClientConfiguration, ClientConfiguration>();
Now let’s follow the rules we defined before to create custom middleware,
public class ClientConfigurationMiddleware
{
private readonly RequestDelegate _next;
public ClientConfigurationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IClientConfiguration clientConfiguration)
{
if (httpContext.Request.Headers.TryGetValue("CLIENTNAME", out StringValues clientName))
{
clientConfiguration.ClientName = clientName.SingleOrDefault();
}
else
{
//Here you can throw exception to force client to send the header
}
clientConfiguration.InvokedDateTime = DateTime.UtcNow;
//Move to next delegate/middleware in the pipleline
await _next.Invoke(httpContext);
}
}
We have injected IClientConfiguration to the InvokeAsync method and we have set the ClientName property of IClientConfiguration by reading the header “CLIENTNAME”, which each client will pass during the API call.
Let’s create the extension method to expose the middleware,
public static class ClientConfigurationExtension
{
public static IApplicationBuilder UseClientConfiguration(this IApplicationBuilder applicationBuilder)
{
return applicationBuilder.UseMiddleware<ClientConfigurationMiddleware>();
}
}
Now we are ready to use this middleware so just add the below snippet in the Configure method,
app.UseClientConfiguration();
Now where ever we have IClientConfiguration injected there we will get the “CLIENTNAME” header value and the InvokedDateTime. Let’s test it.
For testing purpose I am creating a sample controller named ConfigurationController with a GET method which returns ClientConfiguration as such to see are we getting back the header we passed along with the DateTime we invoked,
[Route("api/[controller]")]
[ApiController]
public class ConfigurationController : Controller
{
private readonly IClientConfiguration clientConfiguration;
public ConfigurationController(IClientConfiguration clientConfiguration)
{
this.clientConfiguration = clientConfiguration;
}
[HttpGet]
public ActionResult<ClientConfiguration> GetConfigurationController()
{
return new ClientConfiguration
{
ClientName = clientConfiguration.ClientName,
InvokedDateTime = clientConfiguration.InvokedDateTime
};
}
}
When I invoke the API with the header “CLIENTNAME” I am expecting back the header values we passed and the DateTime invoked. Here it is,

You can clone the complete source code from my git repo,
No comments:
Post a Comment