Introduction
My team was migrating Angular JS 1.5.XX with MVC 5 to .NET Core Web App with Angular 2.0. While doing the same, we realized that we were using HTTP Handlers in the project. And we decided to migrate them too even though this was a bold decision ;-). While migrating, we faced challenges and finally, we were able to migrate. Then I decided why I don't write an article which gives a high-level introduction on the migration. And the outcome is this article :-).
This article provides basic steps to be followed while migrating HTTP Handlers & Modules to ASP.NET Core Middleware. This article will not deep dive into HTTP Handlers & Modules nor on the ASP.NET Core Middleware. I would just use the Response object to display the output in the Web browser. I wanted to keep this article to be very simple. In real time scenarios Handlers & Modules involve critical logic for the application. My intention is to give a high-level introduction to the HTTP Handlers & Module and ASP.NET Core Middleware with code samples as I think that would kick start the understanding of ASP.NET Core Middleware.
In this article, I have used VS 2015 Update 3.
I have planned for this article like below:
- HTTP Handlers
- HTTP Modules
- Create a VS project with a sample HTTP Handler & Module
- ASP.NET Middleware & key methods of it
- Create a VS project with a sample ASP.NET Core Middleware
- Migrating the HTTP Handler & Module created in Step#3 to ASP.NET Middleware
- Key differences between HTTP Handler & Module with ASP.NET Core Middleware
1. HTTP Handlers
HTTP Handlers run in response for each of the requests made to the Web Server. The most common are .aspx or .rss feeds. HTTP Handlers can be made for your own requirements and can render the output to the web browser. They are created by implementing IHttpHandler
's ProcessRequest()
.
public class CustomReportHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
var response = context.Response;
response.Write
("<h2>This handler is written for processing files with .report extension<h2>");
}
#endregion
}
2. HTTP Modules
HTTP Modules are classes which are called for every request that is made to the application. These have access to the life-cycle event of an HTTP pipeline. HTTP Modules can be made for your own requirements by implementing IHttpModule's Init()
.
public class CustomModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += (source, arguments) =>
{
var httpApplication = (HttpApplication)source;
var httpContext = httpApplication.Context;
httpContext.Response.Write("<h3>Custom module started the begin request</h3>");
};
context.EndRequest += (source, arguments) =>
{
var httpApplication = (HttpApplication)source;
var httpContext = httpApplication.Context;
httpContext.Response.Write("<h3>Custom module completed the request</h3>");
};
}
#endregion
3. Create a VS Project with a Sample HTTP Handler & Module
- Open VS 2015 and create a blank solution by name "
MigratingHttpHandlersModulesToMiddleware
". You give any name to it.
- Add a New Project (ASP.NET Web Application) by name "
CustomHandlersModules
" and choose "Empty" template.
- Create a class by name "
CustomReportHandler
" and implement the IHttpHandler
. You can copy the code snippet from the HTTP Handlers section from the above.
- Create a class by name "
CustomModule
" and implement the IHttpModule
. You can copy the code snippet from the HTTP Modules section from the above.
- Update the Web.config file with below entries:
<!---->
<add name="CustomModule" type="CustomHandlersModules.CustomModule"/>
- Press Ctrl + F5 and http://localhost:29342/test.report. Your port might vary.
- You would get the output as:
Custom module started the begin request.
This handler is written for processing files with .report extension.
Custom module completed the request.
at the web browser.
4. ASP.NET Middleware & Its Key Methods
Till now, what we have seen was implementation of Handler & Modules which we all were used to implementing. Now, we will move on to the interesting topic of ASP.Core which is Middleware.
What is Middleware? They are the code which are added to the application pipeline. The code would apply some logic and would handle the request in the pipeline.
In order to implement, we can use 4 methods of the Application Builder which are passed to the Request Delegate.
Run
: This will terminate the http pipeline.
Use
: Adds the middleware to the request pipeline.
Map
: This is used to match request delegates based on a request's path.
MapWhen
: This method supports predicate-based middleware branching, allowing separate pipelines.
When you create a core application, the entry point is Startup.cs. This class has two methods, Configure()
and ConfigureServices()
. Configure()
is a place where all the middleware are defined and executed. ConfigureServices()
is a place for all the Services which the application uses.
You can find more documentation about them at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware.
Now that we have the basic understanding, let's create the ASP.NET Core application.
5. Create a VS Project with a Sample ASP.NET Core Middleware
As we have seen the basic middleware, let's jump start with the migration of the Handler and then with Module.
6. Migrating the HTTP Handler & Module Created in Step#3 to ASP.NET Middleware
6a. Migration HTTP Handler
In this section, we would migrate the Handler which we have created in Step#3.
- Add a class by name "
CustomHanlderMiddleware
" and have the following code in it.
public class CustomHanlderMiddleware
{
private RequestDelegate _next;
public CustomHanlderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync
("<h2>This handler is written for processing files with .report extension<h2>");
}
}
- In order to use the above created
CustomHandlerMiddleware
in the request pipeline, we need to create an extension method on the ApplicationBuilder
. Create a class with name "CustomMiddlewareExtensions
" and place the code like below:
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomHanlderMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomHanlderMiddleware>();
}
}
- At the Startup.cs, place the below code just above the
app.Run()
.
app.Use(async (context, next) => {
await context.Response.WriteAsync("Custom call!!!!! <br/>");
await next.Invoke();
});
app.MapWhen(context => context.Request.Path.ToString().EndsWith(".report"),
appBuilder => {
appBuilder.UseCustomHanlderMiddleware();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
- next.Invoke() method does the invocation of the next request delegate on the pipeline. We can terminate the invocation by not calling the next.
6b. Migration HTTP Module
In this section, we would migrate the Module which we have created in Step#3.
- Add a class by name "
CustomModuleMiddleware
". And have the below code in it:
public class CustomModuleMiddleware
{
private RequestDelegate _next;
public CustomModuleMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync("<h3>Custom module started the begin request</h3>");
await _next.Invoke(context);
await context.Response.WriteAsync("<h3>Custom module completed the request</h3>");
}
}
- At the CustomerMiddlewareExtensions.cs, create an extension method which adds the middleware in the request pipeline. And the class should look like below:
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomHanlderMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomHanlderMiddleware>();
}
public static IApplicationBuilder UseCustomModuleMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomModuleMiddleware>();
}
}
- At the Startup.cs, place the below code just above the
app.Run()
.
app.Use(async (context, next) => {
await context.Response.WriteAsync("Custom call!!!!! <br/>");
await next.Invoke();
});
app.UseCustomModuleMiddleware();
app.MapWhen(context => context.Request.Path.ToString().EndsWith(".report"),
appBuilder => {
appBuilder.UseCustomHanlderMiddleware();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
- Press Ctrl + F5. http://localhost:31736/test.report
And finally, we have migrated the Handlers & Modules to ASP.NET Core Middleware. In order to see the same output, you can comment out the app.Use()
and app.Run()
.
7. Key Differences between HTTP Handler & Module with ASP.NET Core Middleware
- Web.config:
- For Handler & Modules: We have to modify
- For ASP.NET Core: No need to modify
- Application Life Cycle:
- For Handler & Modules: We have it
- For ASP.NET Core: It does not exists
- Request Pipeline Termination:
- For Handler & Modules:
CompleteRequest()
would terminate the request
- For ASP.NET Core: By not passing the request delegate to the next component
Points of Interest
- We consider ASP.NET Core Middleware is compination of HTTP Handler & Module.
- Placing of the
app.Use()
method at the Starup.cs configure()
is key for the execution of the middleware.
- ASP.NET Core comes with different types out-of-box of Middleware. For e.g.. Authentication, Sessions, CORS, etc.
End Note
I have placed the code on github which uses different middleware mainly sessions (In Memory) and passing of the data. The code can be found at https://github.com/psdinesh/MigratingHttpHandlersModulesAspNetCoreMiddleware.