以前ASP.NET中使用的HTTP Modules及HTTP Handlers,在ASP.NET Core中已不复存在,取而代之的是Middleware。Middleware除了简化了HTTP Modules/Handlers的使用方式,还带入了Pipeline的概念。
本篇将介绍ASP.NET Core的Middleware概念及用法。git
ASP.NET Core在Middleware的官方说明中,使用了Pipeline这个名词,意指Middleware像水管同样能够串联在一块儿,全部的Request及Response都会层层通过这些水管。
用图例能够很容易理解,以下图:github
Middleware的注册方式是在Startup.cs的Configure
对IApplicationBuilder
使用Use
方法注册。
大部分扩展的Middleware也都是以Use开头的方法注册,例如:浏览器
一个简单的Middleware 范例。以下:bash
Startup.csapp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace MyWebsite
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("First Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("First Middleware out. \r\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Second Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("Second Middleware out. \r\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Third Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("Third Middleware out. \r\n");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World! \r\n");
});
}
}
}
用浏览器打开网站任意连结,输出结果: async
First Middleware in.
Second Middleware in.
Third Middleware in.
Hello World!
Third Middleware out.
Second Middleware out.
First Middleware out.
在Pipeline的概念中,注册顺序是很重要的事情。请求通过的顺序必定是先进后出。网站
Request 流程以下图: ui
Middleware 也能够做为拦截使用,以下:this
Startup.csspa
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace MyWebsite
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("First Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("First Middleware out. \r\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Second Middleware in. \r\n");
// 水管阻塞,封包不日后送 var condition = false; if (condition) { await next.Invoke(); }
await context.Response.WriteAsync("Second Middleware out. \r\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Third Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("Third Middleware out. \r\n");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World! \r\n");
});
}
}
}
输出结果:
First Middleware in.
Second Middleware in.
Second Middleware out.
First Middleware out.
在Second Middleware 中,由于没有达成条件,因此封包也就不在日后面的水管传送。流程如图:
Run
是Middleware的最后一个行为,以上面图例来讲,就是最末端的Action。
它不像Use
能串联其余Middleware,但Run
仍是能完整的使用Request及Response。
Map
是能用来处理一些简单路由的Middleware,可依照不一样的URL指向不一样的Run
及注册不一样的Use
。
新增一个路由以下:
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace MyWebsite
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("First Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("First Middleware out. \r\n");
});
// app.Use(async (context, next) =>
// {
// await context.Response.WriteAsync("Second Middleware in. \r\n");
// // 水管阻塞,封包不日后送
// var condition = false;
// if (condition)
// {
// await next.Invoke();
// }
// await context.Response.WriteAsync("Second Middleware out. \r\n");
// });
app.Map("/second", mapApp => { mapApp.Use(async (context, next) => { await context.Response.WriteAsync("Second Middleware in. \r\n"); await next.Invoke(); await context.Response.WriteAsync("Second Middleware out. \r\n"); }); mapApp.Run(async context => { await context.Response.WriteAsync("Second. \r\n"); }); });
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Third Middleware in. \r\n");
await next.Invoke();
await context.Response.WriteAsync("Third Middleware out. \r\n");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World! \r\n");
});
}
}
}
开启网站任意连结,会显示:
First Middleware in.
Third Middleware in.
Hello World!
Third Middleware out.
First Middleware out.
开启网站http://localhost:5000/second
,则会显示:
First Middleware in.
Second Middleware in.
Second.
Second Middleware out.
First Middleware out.
若是Middleware所有都写在Startup.cs,代码将很难维护,因此应该把自定义的Middleware逻辑独立出来。
创建Middleware类不须要额外继承其它类或接口,通常的类便可,例子以下:
FirstMiddleware.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MyWebsite
{
public class FirstMiddleware
{
private readonly RequestDelegate _next;
public FirstMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync($"{nameof(FirstMiddleware)} in. \r\n");
await _next(context);
await context.Response.WriteAsync($"{nameof(FirstMiddleware)} out. \r\n");
}
}
}
在Startup.Configure
注册Middleware就能够套用到全部的Request。以下:
Startup.cs
// ...
public class Startup
{
// ...
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<FirstMiddleware>();
// ...
}
}
Middleware 也能够只套用在特定的Controller 或Action。注册方式以下:
Controllers\HomeController.cs
// ..
[MiddlewareFilter(typeof(FirstMiddleware))]
public class HomeController : Controller
{
// ...
[MiddlewareFilter(typeof(SecondMiddleware))]
public IActionResult Index()
{
// ...
}
}
大部分扩展的Middleware都会用一个静态方法包装,如:UseMvc()
、UseRewriter()
等。
自定义的Middleware固然也能够透过静态方法包,范例以下:
Extensions\CustomMiddlewareExtensions.cs
using Microsoft.AspNetCore.Builder;
namespace MyWebsite
{
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseFirstMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<FirstMiddleware>();
}
}
}
注册Extension Middleware 的方式以下:
Startup.cs
// ...
public class Startup
{
// ...
public void Configure(IApplicationBuilder app)
{
app.UseFirstMiddleware();
// ...
}
}
ASP.NET Core Middleware Fundamentals
Creating Custom Middleware In ASP.Net Core
老司机发车啦:https://github.com/SnailDev/SnailDev.NETCore2Learning