Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article shows how to migrate existing ASP.NET HTTP handlers from system.webserver to ASP.NET Core middleware.
Handlers revisited
Before proceeding to ASP.NET Core middleware, let's first recap how HTTP handlers work:
Handlers are:
Classes that implement IHttpHandler
Used to handle requests with a given file name or extension, such as .report
Configured in Web.config
From handlers to middleware
Middleware are simpler than HTTP handlers:
Handlers, Web.config (except for IIS configuration) and the application life cycle are gone
The roles of handlers have been taken over by middleware
Middleware are configured using code rather than in Web.config
- Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.
- Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.
Middleware are very similar to handlers:
- Able to create their own HTTP response
Migrating handler code to middleware
An HTTP handler looks something like this:
// ASP.NET 4 handler
using System.Web;
namespace MyApp.HttpHandlers
{
public class MyHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string response = GenerateResponse(context);
context.Response.ContentType = GetContentType();
context.Response.Output.Write(response);
}
// ...
private string GenerateResponse(HttpContext context)
{
string title = context.Request.QueryString["title"];
return string.Format("Title of the report: {0}", title);
}
private string GetContentType()
{
return "text/plain";
}
}
}
In your ASP.NET Core project, you would translate this to a middleware similar to this:
// ASP.NET Core middleware migrated from a handler
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace MyApp.Middleware
{
public class MyHandlerMiddleware
{
// Must have constructor with this signature, otherwise exception at run time
public MyHandlerMiddleware(RequestDelegate next)
{
// This is an HTTP Handler, so no need to store next
}
public async Task Invoke(HttpContext context)
{
string response = GenerateResponse(context);
context.Response.ContentType = GetContentType();
await context.Response.WriteAsync(response);
}
// ...
private string GenerateResponse(HttpContext context)
{
string title = context.Request.Query["title"];
return string.Format("Title of the report: {0}", title);
}
private string GetContentType()
{
return "text/plain";
}
}
public static class MyHandlerExtensions
{
public static IApplicationBuilder UseMyHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyHandlerMiddleware>();
}
}
}
This middleware is very similar to the middleware corresponding to modules. The only real difference is that here there's no call to _next.Invoke(context)
. That makes sense, because the handler is at the end of the request pipeline, so there will be no next middleware to invoke.
Migrating handler insertion into the request pipeline
Configuring an HTTP handler is done in Web.config and looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
<system.webServer>
<handlers>
<add name="MyHandler" verb="*" path="*.report" type="MyApp.HttpHandlers.MyHandler" resourceType="Unspecified" preCondition="integratedMode"/>
</handlers>
</system.webServer>
</configuration>
You could convert this by adding your new handler middleware to the request pipeline in your Startup
class, similar to middleware converted from modules. The problem with that approach is that it would send all requests to your new handler middleware. However, you only want requests with a given extension to reach your middleware. That would give you the same functionality you had with your HTTP handler.
One solution is to branch the pipeline for requests with a given extension, using the MapWhen
extension method. You do this in the same Configure
method where you add the other middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMyMiddleware();
app.UseMyMiddlewareWithParams();
var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
app.UseMyMiddlewareWithParams(myMiddlewareOptions);
app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
app.UseMyTerminatingMiddleware();
// Create branch to the MyHandlerMiddleware.
// All requests ending in .report will follow this branch.
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".report"),
appBranch => {
// ... optionally add more middleware to this branch
appBranch.UseMyHandler();
});
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".context"),
appBranch => {
appBranch.UseHttpContextDemoMiddleware();
});
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
MapWhen
takes these parameters:
A lambda that takes the
HttpContext
and returnstrue
if the request should go down the branch. This means you can branch requests not just based on their extension, but also on request headers, query string parameters, etc.A lambda that takes an
IApplicationBuilder
and adds all the middleware for the branch. This means you can add additional middleware to the branch in front of your handler middleware.
Middleware added to the pipeline before the branch will be invoked on all requests; the branch will have no impact on them.
For additional details, see the middleware documentation for additional ways to use middleware to replace your usage of handlers.
Migrating to the new HttpContext
The Invoke
method in your middleware takes a parameter of type HttpContext
:
public async Task Invoke(HttpContext context)
HttpContext
has significantly changed in ASP.NET Core. For detailed information on how to translate the most commonly used properties of System.Web.HttpContext
to the new Microsoft.AspNetCore.Http.HttpContext
, see Migrate from ASP.NET Framework HttpContext to ASP.NET Core.
Additional resources
ASP.NET Core