ASP.NETCore学习记录(二) —— ASP.NET Core 中间件
ASP.NET Core 中间件
什么是中间件 ?
我们知道在 ASP.NET 中,有一个面向切面的请求管道,由22个主要的事件构成,能够让我们在往预定的执行顺序里面添加自己的处理逻辑。一般采取两种方式:一种是直接在 Global.asax 中对应的方法中直接添加代码。一种是是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监听,并通过 HttpHandler 进入到我们的应用程序中。而在 ASP.NET Core 中,对请求管道进行了重新设计,通过使用一种称为中间件的方式来进行管道的注册,同时也变得更加简洁和强大。关于 HTTP请求流程和管道模型可以看这篇博客
返回顶部
ASP.NET Core 中定义了一个 RequestDelegate 委托,来表示请求管道中的一个步骤,而对请求管道的注册是通过 Func
返回顶部
ASP.NET Core 2.1具有新功能)。然后就是静态文件中间件 UseStaticFiles,静态文件中间件不提供授权检查,由它提供的任何文件,包括那些位于wwwroot下的文件都是公开可被访问的。UseCookiePolicy 是使用ASP.NET Core中的Cookie策略(详解Microsoft.AspNetCore.CookiePolicy),管道的最后执行的也就是MVC框架。
顺序
向 Startup.Configure 方法添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此排序对于安全性、性能和功能至关重要。请求在每一步都可能被短路,所以我们要以正确的顺序添加中间件,如异常处理,我们需要添加在最开始,这样我们就能第一时间捕获异常,以及后续中间可能发生的异常,然后最终做处理返回。
最简单的ASP.NET应用程序(空白Web模板)是使用单个请求委托来处理所有请求。事实上,在这种情况下,并不存在所谓的“管道”,针对每个HTTP请求都调用单个匿名函数来处理。
public void Configure(IApplicationBuilder app)
{
// 使用单个请求委托来处理所有请求
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
上方代码中 app.Run() 会中断管道,调用单个匿名函数来处理HTTP请求。在下面的例子中,Run了两个委托,只有第一个委托(“Hello,World!”)会被运行。
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Welcome!");
});
}
通过运行项目,发现确实只有在第一个委托执行了,app.Run 终止了管道。
你可以将多个请求委托 app.Use 连接在一起,next参数表示管道内的下一个请求委托。在管道中,可以通过不调用next参数来终止或短路管道。通常可以在执行下一个委托之前和之后执行一些操作,如下例所示:
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Handing Request.\r\n");
// 调用管道中的下一个委托
await next.Invoke();
await context.Response.WriteAsync("Finish Handing Request.\r\n");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello from Middleware\r\n");
});
}
运行项目,在浏览器中访问出现如下结果:
返回顶部
http://localhost:52831 的请求和响应。
| 请求 | 响应 |
|---|---|
| http://localhost:52831 | Hello from non-Map delegate. |
| http://localhost:52831/map1 | Map Test 1 |
| http://localhost:52831/map2 | Map Test 2 |
| http://localhost:52831/map3 | Hello from non-Map delegate. |
除了基于路径的映射外,MapWhen 方法还支持基于谓语的中间件分支,允许以非常灵活的方式构建单独的管道。任何 Func
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync(" Hello from non-Map delegate.
");
});
}
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
}
使用了上面的设置后,任何包含请求字符 branch 的请求将使用定义于 HandleBranch 方法内的管道,如果没有包含查询字符串 branch 的请求,将被 app.Run 所定义的委托处理。
| 请求 | 响应 |
|---|---|
| http://localhost:52831 | Hello from non-Map delegate. |
| http://localhost:52831/?branch=1 | Branch used = 1 |
| http://localhost:52831/?branch=2 | Branch used = 2 |
| http://localhost:52831/?branch | Branch used = |
另外还可以嵌套 Maps:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a"
//...
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b"
//...
});
});
Map 也可以一次匹配多个片段,例如:
app.Map("/level1/level2", HandleMultiSeg); // "/level1/level2"
以下 Startup.Configure 方法将为常见应用方案添加中间件组件:
| 中间件 | 描述 |
|---|---|
| 身份验证(Authentication) | 提供身份验证支持 |
| 跨域资源共享(CORS) | 配置跨域资源共享 |
| 响应支持(Response Caching) | 提供缓存响应支持 |
| 响应压缩(Response Compression) | 提供响应压缩支持 |
| 路由(Routing) | 定义和约束请求路由 |
| 会话(Session) | 提供对管理用户会话(session)的支持 |
| 静态文件(Static Files) | 为静态文件和目录浏览提供服务提供支持 |
| URL Rewriting Middleware | 用于重写 Url,并将请求重定向的支持 |
更多中间件组件可以到 Microsoft 文档 上查看。
返回顶部
显式依赖原则 ,并在其构造函数中暴露所有的依赖项。中间件能够利用 UseMiddleware
下面来实现一个记录IP的中间件。
① 新建一个 ASP.NET Core WebApplication 项目,选择空的模板。
然后为项目添加—个 Microsoft.Extensions.Logging.Console。
NuGet命令行执行(请使用官方源):
Install-Package Microsoft.Extensions.Logging.Console
② 新建一个类 RequestIPMiddleware.es,将中间件委托移动到类:
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace middlewareDemo
{
public class RequestIPMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public RequestIPMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger();
}
public async Task Invoke(HttpContext context)
{
_logger.LogInformation("User IP:" + context.Connection.RemoteIpAddress.ToString());
await _next.Invoke(context);
}
}
}
③ 再新建—个 RequestIPExtensions.cs,以下扩展方法通过 IApplicationBuilder 公开中间件:
using Microsoft.AspNetCore.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace middlewareDemo
{
public static class RequestIPExtensions
{
public static IApplicationBuilder UseRequestIP(this IApplicationBuilder builder)
{
return builder.UseMiddleware();
}
}
}
这样就编写好了一个中间件。
④ 使用中间件。在 Startup.cs 中添加 app.UseRequestIP() 来使用中间件:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseRequestIP();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
然后运行程序,这里选择使用 Kestrel
访问:http://localhost:5000/
成功运行,到这里我们还可以对这个中间件进行进一步改进,增加更多的功能,如限制访问等。
Microsoft 文档 ASP.NET Core 中间件
《ASP.NET Core 跨平台开发从入门到实战》
返回顶部