本文介绍如何将现有 ASP.NET HTTP 处理程序从 system.webserver 迁移到 ASP.NET Core 中间件。
处理程序再探讨
在继续 ASP.NET 核心中间件之前,让我们先回顾一下 HTTP 处理程序的工作原理:

处理程序是:
实现 IHttpHandler 的类
用于处理具有给定文件名或扩展名的请求,例如 .report
在 Web.config 中配置
从处理程序到中间件
中间件比 HTTP 处理程序更简单:
处理程序、Web.config(IIS 配置除外)和应用程序生命周期都消失了
中间件接管了处理程序的角色
中间件是使用代码而不是在 Web.config 中配置的
- 通过管道分支 ,可以基于 URL 以及请求标头、查询字符串等将请求发送到特定中间件。
- 通过管道分支 ,可以基于 URL 以及请求标头、查询字符串等将请求发送到特定中间件。
中间件与处理程序非常相似:
- 能够创建自己的 HTTP 响应

将处理程序代码迁移到中间件
HTTP 处理程序如下所示:
// 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";
}
}
}
在 ASP.NET Core 项目中,你将将其转换为如下所示的中间件:
// 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>();
}
}
}
此中间件与与模块对应的中间件非常相似。 唯一真正的区别是这里没有调用 _next.Invoke(context)。 这很有意义,因为处理程序位于请求管道的末尾,因此不会有下一个要调用的中间件。
将处理程序插入迁移到请求管道中
配置 HTTP 处理程序是在 Web.config 完成的,如下所示:
<?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>
可以通过在 Startup 类中将新处理程序中间件添加到请求管道来对此进行转换,类似于从模块转换的中间件。 此方法的问题是,它会将所有请求发送到新的处理程序中间件。 但是,你仅希望具有给定扩展名的请求到达中间件。 这将为你提供与 HTTP 处理程序相同的功能。
一种解决方案是使用 MapWhen 扩展方法为具有给定扩展名的请求对管道进行分支。 在添加其他中间件的相同 Configure 方法中执行此操作:
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 采用以下参数:
一个 lambda,它采用
HttpContext,并在请求应沿着分支向下前进时返回true。 这意味着,您不仅可以基于请求的扩展名进行分支,还可以根据请求头、查询字符串参数等进行分支。一个 lambda,它采用
IApplicationBuilder并为分支添加所有中间件。 这意味着可以将其他中间件添加到处理程序中间件前面的分支。
中间件在对所有请求调用分支之前添加到管道;分支对它们没有影响。
有关更多详细信息,请参阅 中间件文档 ,了解使用中间件替换处理程序用法的其他方法。
迁移到新 HttpContext
Invoke中间件中的方法接受一个类型为HttpContext的参数。
public async Task Invoke(HttpContext context)
HttpContext 在 ASP.NET Core 中发生了重大变化。 有关如何将最常用的System.Web.HttpContext属性翻译到新的Microsoft.AspNetCore.Http.HttpContext属性的详细信息,请参阅从 ASP.NET Framework HttpContext 迁移到 ASP.NET Core。