ASP.NET Core笔记(5) - 中间件

  • 中间件管道模型
  • 中间件的配置
  • 自定义中间件

中间件是一类装配在应用管道的代码,负责处理请求和响应。每一个中间件均可在管道中的下一个组件先后执行工做,并选择是否将请求传递到管道中的下一个中间件。在Startup.Configure方法中能够进行中间件的装配。app

中间件管道模型

中间件管道模型以下图所示:
中间件管道模型async

ASP.NET Core请求管道包含一系列请求委托,沿黑色箭头依次被调用执行,每一个委托都可在下一个委托先后执行操做。这种模型也被形象地称为“俄罗斯套娃”。函数

一个中间件能够是匿名方法的显示嵌入到管道中,也能够封装为单独的类便于重用,嵌入式的中间件就像这样:ui

public void Configure(IApplicationBuilder app)
{
	app.Run(async context =>
	{
		await context.Response.WriteAsync("Hello, World!");
	});
}

中间件的配置

配置中间件会使用到三个扩展方法:this

  • Use
  • Run
  • Map

Use

Use用来将多个中间件按添加顺序连接到一块儿:code

app.Use(async (context, next) =>
{
	await context.Response.WriteAsync("middleware1 begin\r\n");
	await next.Invoke();
	await context.Response.WriteAsync("middleware1 end\r\n");
});

app.Use(async (context, next) =>
{
	await context.Response.WriteAsync("middleware2 begin\r\n");
	await next.Invoke();
	await context.Response.WriteAsync("middleware2 end\r\n");
});

app.Run(async context =>
{
	await context.Response.WriteAsync("end of pipeline.\r\n");
});

这三个中间件配置到管道后,输出的结果与管道模型的图示是一致的:中间件

middleware1 begin
middleware2 begin
end of pipeline.
middleware2 end
middleware1 end

能够看到除了最后一个中间件,前面的中间件都调用了await next.Invoke(),next参数表示管道中的下一个委托,若是不调用 next,后面的中间件就不知执行,这称为管道的短路。
一般中间件都应该自觉调用下一个中间件,但有的中间件会故意形成短路,好比受权中间件、静态文件中间件等。blog

Run

Run的委托中没有next参数,这就意味着它会称为最后一个中间件(终端中间件),此外能够故意短路的Use委托也可能会成为终端中间件。ip

Map,提供了建立管道分支的能力

Map扩展用做约定来建立管道分支,会基于给定请求路径的匹配项来建立请求管道分支,若是请求路径以给定路径开头,则执行分支。字符串

private static void HandleMapTest1(IApplicationBuilder app)
{
	app.Run(async context =>
	{
		await context.Response.WriteAsync("Map Test 1");
	});
}

private static void HandleMapTest2(IApplicationBuilder app)
{
	app.Run(async context =>
	{
		await context.Response.WriteAsync("Map Test 2");
	});
}

public void Configure(IApplicationBuilder app)
{
	app.Map("/map1", HandleMapTest1);

	app.Map("/map2", HandleMapTest2);

	app.Run(async context =>
	{
		await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
	});
}

这段代码为管道建立了三个分支:

URL Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
Map还支持嵌套
app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});
MapWhen,知足条件时建立分支
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               branch => {
    });

此外,UseWhen也能够根据条件建立分支,它与MapWhen的区别在于,使用UseWhen建立的分支若是不包含终端中间件,则会从新汇入主管道。

自定义中间件

一般使用内置的中间件可知足大多数场合,但若是有须要也能够建立自定义中间件。
假设有这样一个嵌入式中间件,能够经过查询字符串设置当前请求的区域,好比https://localhost:5000/?culture=zh-cn,会将CurrentCulture设置为Chinese Simplified。如今要将其封装为可重用的独立的中间件。

public void Configure(IApplicationBuilder app)
{
	app.Use(async (context, next) =>
	{
		var cultureQuery = context.Request.Query["culture"];
		if (!string.IsNullOrWhiteSpace(cultureQuery))
		{
			var culture = new CultureInfo(cultureQuery);

			CultureInfo.CurrentCulture = culture;
			CultureInfo.CurrentUICulture = culture;
		}

		// Call the next delegate/middleware in the pipeline
		await next();
	});

	app.Run(async (context) =>
	{
		await context.Response.WriteAsync(
			$"Hello {CultureInfo.CurrentCulture.DisplayName}");
	});

}

在开始封装前,先了解一下对中间件的要求:

  • 中间件必须具备类型为RequestDelegate的参数的公共构造函数,以前代码中看到next.Invoke(),其中next就是下一个中间件的RequestDelegate;
  • 名为 Invoke 或 InvokeAsync 的公共方法。并且这个方法的返回类型为Task,且第一个参数的必须是HttpContext,其它的参数由DI容器解析。

基于上述要求,编写的中间件为:

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline
        //await _next.Invoke(context);
        await _next(context);
    }
}

而后就可使用了:

app.UseMiddleware<RequestCultureMiddleware>();

还能够进一步封装为IApplicationBuilder的扩展方法:

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

而后就能够像内置的中间件同样了:

app.UseRequestCulture();
相关文章
相关标签/搜索