十六、OWIN 和中间件
在本章中,我们将介绍:
- 了解 OWIN, Katana 和新的 ASP.NET Core HTTP 管道
- 使用内联中间件代码作为匿名方法—使用、运行、映射和映射时
- 创建一个可重用的中间件组件
- 将 HTTP 处理程序迁移到中间件
- 将 HTTP 模块迁移到中间件
了解 OWIN, Katana 和新的 ASP.NET Core HTTP 管道
在本教程中,我们将深入了解 OWIN、Katana 和 ASP 中的中间件管道.NET 的核心。
一个解耦的抽象
开放的。net Web 接口(OWIN)是一个标准,它定义了 Web 服务器和。net Web 应用之间的接口。 实现 OWIN 允许我们在 web 应用、主机和服务器之间创建一个抽象,并将。net web 应用与 IIS 分离。 例如,要将 WebAPI 中的主机更改为在控制台应用或 Windows 服务中自托管的主机,请更改 ASP。 asp.net MVC 或 WebAPI 与 NancyFx。
主机是操作系统中加载服务器并创建管道的进程。 服务器在特定的端口上监听请求,将其重定向到管道,并返回对应用生成的请求的响应。 应用对收到的请求进行线程化并生成响应。
Katana -微软 OWIN 的实现
在 ASP.NET Core,为了从 IIS 中分离。NET 应用,我们可以使用 OWIN。 为了宿主 ASP.NET Web API 之外的 IIS,我们使用 Katana 项目(微软 OWIN 实现)。
使用 Katana,使用特定于 win 的库,我们仍然使用 Windows 环境。 ASP.NET MVC 5 与 IIS 太紧密了,然而,OWIN 为我们提供了一个新的 HTTP 管道来与 IIS 解耦。 自 ASP.NET Web API 2,将我们的 API 从 IIS 和 asp.net 中解耦.NET 管道生命周期,我们可以使用 OWIN。
为了实现这一点,使用 Katana 管道和类,我们使用一个Startup.cs
文件而不是经典的Global.asax
文件来配置我们的 API,并将主机更改为自托管(控制台、Windows 服务或 Azure worker 角色)。
另一种从服务器上解耦。net 应用的方法是使用 OWIN 作为 HTTP 管道,使用 NancyFx 替代 ASP.NET MVC 或 Web API。 我们可以使用一个不可知的方法来抽象 MVC 和 WebAPI,用 Nancy 来代替它们。 Nancy 是一个轻量级的框架,可以在 Mono、。net Core 或。net framework 上创建 HTTP 服务。
当请求到达主机时,在它到达应用代码之前,它要经过一个管道,该管道由 ASP。 由HttpApplication
类定义的 NET/IIS 管道。 它通过 HTTP 处理程序和模块的组合。 我们可以通过创建新的 HTTP 模块和处理程序来在这个管道上聚合处理,但是我们对它没有太多的控制。
一个新的基于 ASP 的 OWIN 实现.NET Core
ASP.NET Core,我们现在不知道主机和 web 服务器。 我们使用与 OWIN 相同的原理,这意味着在应用、服务器和主机之间使用抽象。 它的新特点是,我们可以通过向管道中添加中间件组件来为应用组成管道。
ASP.NET Core 尊重 OWIN 原则,但不是 OWIN。 区别之一是,它将来自请求的所有传入数据映射到所有中间件(内置的、行创建的或组件创建的)可用的 HttpContext 对象。 另一个不同于以前版本的 ASP。 HttpContext
对象现在与 IIS 和 System 无关了。 Web,并且更轻量级。 然而,我们总是有机会使用 OWIN 库,因此在 ASP 中使用 OWIN 管道.NET MVC 核心,就像我们使用 ASP.NET Web API 2.2。
ASP.NET Core 管道
在 ASP.NET Core 管道,我们可以通过两种方式添加中间件:匿名函数中的内联代码,或者可重用的中间件组件。 两者都在Startup.cs
中的Configure()
方法中以顺序的方式添加到管道中。
当调用Configure()
方法时,就准备好 HTTP 管道并设置它来处理传入的请求。 这些传入请求被传递到添加到管道中的每个中间件中,并由它们执行线程。
在下一篇文章中,我们将学习中间件如何与内联代码一起工作。 中间件的内联代码并不是最佳实践。 更重要的是创建中间件类并使用app.UseMiddleware
方法将它们添加到管道中,原因多种多样(可重用性、清晰性等等)。 它们的工作原理是一样的。
中间件
在计算机世界中,中间件是一种软件组件,它将在不同的应用之间以不同的方式交互、互操作、交换和共享数据。 有服务、消息或面向对象的中间件。
ASP.NET Core,中间件将是添加到 HTTP 管道中的处理和组件,由Startup.cs
中的Configure()
方法表示。
这个中间件使我们能够完全控制替代传统 ASP 的管道.NET 应用生命周期以及所有 HTTP 模块和处理程序。 我们将选择他们将被执行的顺序。
已经有一些内置的中间件,但是我们必须创建自己的,或者创建一些来以更细的粒度管理现有的中间件(授权、身份验证、会话、缓存、日志、路由、异常处理等等)。
HTTP 模块和 HTTP 处理程序
与 HttpHandlers 和 HttpModules 不同,中间件是通过编程方式创建和使用的,无需配置文件。 我们现在不受 IIS 和System.Web
的影响。
让我们来看看一些关于HttpHandlers
和HttpModules
的事情:
- 在到达生成响应的处理程序之前和/或在生成响应之后,为每个请求运行
HttpModule
- 处理程序处理请求并为给定的文件扩展名生成响应
使用内联中间件代码作为匿名方法—使用、运行、映射和映射时
在本教程中,我们将学习如何在Startup.cs
的Configure()
方法中使用内联中间件代码。
准备
我们创建一个空的 ASP.NET Core web 应用与 Visual Studio,使用。NET Core 或。NET Framework。
怎么做……
我们将创建一个新项目,并使用不同类型的中间件:
- 首先,让我们使用
app.Use
:
public void Configure(IApplicationBuilder app)
{
...
app.Use(async (context, next) =>
{
await context.Response.WriteAsync
("First Inline middleware before Handlen");
await next.Invoke();
await context.Response.WriteAsync
("First Inline middleware after Handle");
});
...
}
next.Invoke()
将调用管道中的下一个中间件。
我们可以添加任意数量的app.Use
方法。 它们将按照添加到管道上的顺序执行。
与 HTTP 模块进行类比,代码执行前等待next.Invoke()
与BeginRequest
方法中的代码在一个类实现IHttpModule
,和代码执行后等待next.Invoke()
与EndRequest
方法中的代码。
Use()
方法中的匿名函数有两个参数:当前 HTTP 上下文和按约定接下来调用的委托。 此委托用于调用下一个委托。 如果我们不调用app.Run
中的next.Invoke()
方法,我们就会短路管道,接下来的中间件(内联的、静态的或自定义的)将不会被执行。
- 第二,让我们用
app.Run
:
public void Configure(IApplicationBuilder app)
{
...
app.Use(async (context, next) =>
{
await context.Response.WriteAsync
("First Inline middleware before Handlen");
await next.Invoke();
await context.Response.WriteAsync
("First Inline middleware after Handle");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Run middleware n");
});
...
}
app.Run
使管道短路。 管道知道app.Run
方法中的代码将是在管道上执行的最后一个中间件代码,并且将生成响应。 即使在代码之后添加了一些中间件(内联或类),它也不会被执行。
- 第三,我们使用
app.Map
:
public void Configure(IApplicationBuilder app)
{
...
app.Map("/oneurlsegment", (appBuilder) =>
{
appBuilder.Use(async (context, next) =>
{
await context.Response.WriteAsync
("Third Inline middleware before Handle n");
await next.Invoke();
await context.Response.WriteAsync
("Third Inline middleware after Handle n");
});
...
}
}
Map
方法将对 URL 分支执行一个处理。
- 第四,让我们用
app.MapWhen
:
public void Configure(IApplicationBuilder app)
{
...
app.MapWhen(context =>
context.Request.Query.ContainsKey("q1"), (appBuilder) =>
{
appBuilder.Use(async (context, next) =>
{
await context.Response.WriteAsync
("Fourth Inline middleware before Handle n ");
await next.Invoke();
await context.Response.WriteAsync
("Fourth Inline middleware after Handle n ");
});
...
}
}
方法将执行条件代码检查请求参数。
创建一个可重用的中间件组件
在这个食谱中,我们将学习如何使用环境、脚本和链接标签助手。
准备
我们创建一个空的 ASP.NET Core web 应用与 Visual Studio 和。NET Core 或。NET Framework。
怎么做……
我们将创建一个新项目并编写第一个中间件。 通过这个项目,我们可以观察中间件的执行机制。
- 首先,让我们创建一个
middleware
类:
public class MyMiddleware1
{
private readonly RequestDelegate _next;
public MyMiddleware1(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync
("Hello from first middleware before Request n");
await _next(httpContext);
await httpContext.Response.WriteAsync
("Hello from first middleware after Request n");
}
}
一个middleware
类不继承任何类或接口,但必须遵守一些规则:
- 现在我们通过
app.UseMiddleware<MyMiddleware>()
方法将其添加到管道中:
public class Startup
{
...
public void Configure
(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<MyMiddleware1>();
}
...
}
- 然而,更优雅的方法是为中间件创建一个
extension
方法:
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseMiddleware1
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware1>();
}
}
将它添加到管道中,替换之前的语法:
public class Startup
{
...
public void Configure
(IApplicationBuilder app, IHostingEnvironment env)
{
//app.UseMiddleware<MyMiddleware1>();
app.UseMiddleware1();
}
...
}
- 在我们创建的这个可重用中间件中,我们可以通过
Startup.cs
的ConfigureServices
方法将依赖注入注入的任何服务或类添加到中间件构造函数中:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IDataRepository
{
List<Product> GetAll();
}
public class ProductRepository : IDataRepository
{
public List<Product> GetPromotedProducts()
{
...
}
}
public class StoredDataMiddleware
{
RequestDelegate _next;
IMemoryCache _cache;
IRepository _repo;
public StoredDataMiddleware(RequestDelegate next,
IMemoryCache cache, IRepository repo)
{
_next = next;
_cache = cache;
_repo = repo;
}
public async Task Invoke(HttpContext context)
{
var products = _repo.GetPromotedProducts();
_cache.Set<List<Product>>("PromotedProducts", products);
await context.Response.WriteAsync
("I put in cache datas in a middleware n");
await _next.Invoke(context);
}
}
在本例中,我们通过中间件在缓存中存储一个产品列表。
- 最后,我们来看看如何配置
Startup.cs
:
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddScoped<IDataRepository, ProductRepository>();
}
public void Configure(IApplicationBuilder app,
IHostingEnvironment env, IMemoryCache cache)
{
app.UseStoredDataMiddleware();
//app.UseMiddleware<MyMiddleware2>();
}
...
}
将 HTTP 处理程序迁移到中间件
在这个食谱中,我们将学习如何转换一个 ASP。 asp.net HTTP 处理程序.NET Core 的中间件。
在开始之前,让我们回顾一下什么是 HTTP 处理程序。 HTTP 处理程序处理来自 ASP 的传入请求.NET/IIS 管道,并为给定的扩展文件(.aspx
、.html
、.jpg
,等等)生成响应。 ASP.NET MVC 中,我们使用了一个名为MVCHandler
的特定处理程序,它确保在请求 URL 中指定路由的控制器中存在action
方法。
准备
我们创建一个空的 ASP.NET Core web 应用与 Visual Studio 和。NET Core 或。NET Framework。
怎么做……
我们将创建一个新项目并编写第一个中间件。 通过这个项目,我们可以观察中间件的执行机制。
- 首先,让我们看一下 HTTP 处理程序的结构:
public class MyHttpHandler : IHttpHandler
{
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context)
{
context.Response.Write("This page pass by MyHttpHandler <br />");
}
}
- 现在,让我们看看
Web.config
配置:
<system.webServer>
<handlers>
<add name="MyReportHandler" verb="*" path="*.report" type="MyApp.HttpHandlers.MyReportHandler" resourceType="Unspecified" />
</handlers>
</system.webServer>
我们必须指定名称、此处理程序的授权 HTTP 谓词、路径(即已命名的扩展文件)和类型(即名称空间的处理程序)。
- 在 ASP 中等效的代码.NET Core 可能如下:首先,我们创建与处理程序相对应的中间件。 我们可以通过四种方式做到这一点:
public class MyReportMiddleware
{
private readonly RequestDelegate _next;
public MyReportMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
return httpContext.Response.WriteAsync("this .report file will be executed and short circuit the pipeline");
// we doesn't call next because we want to short circuit the
// pipeline in order to act as a classic HttpHandler
//return _next(httpContext);
}
}
// Extension method used to add the middleware
// to the HTTP request pipeline.
public static class MyReportMiddlewareExtensions
{
public static IApplicationBuilder UseMyReportMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyReportMiddleware>();
}
}
public void Configure(IApplicationBuilder app)
{
...
app.MapWhen(
context =>
context.Request.Path.ToString().EndsWith(".report"),
appBranch =>
{
appBranch.UseMyReportMiddleware();
});
...
}
private static void HandleBranchForReport
(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync
("this .report file will be executed and short circuit the pipeline");
});
}
public void Configure(IApplicationBuilder app)
{
...
app.MapWhen(
context =>
context.Request.Path.ToString().EndsWith(".report"),
HandleBranchForReport);
...
}
public void Configure(IApplicationBuilder app)
{
...
app.MapWhen(
context =>
context.Request.Path.ToString().EndsWith(".report"),
appBranch =>
{
appBranch.Run(async (context) =>
{
await context.Response.WriteAsync
("this .report file will be executed and short circuit the pipeline");
});
});
...
}
// Middleware class
public class MySecondReportMiddleware
{
private readonly RequestDelegate _next;
public MySecondReportMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
if(httpContext.Request.Path.ToString()
.EndsWith(".report"))
return httpContext.Response.WriteAsync
("this .report file will be executed and
short circuit the pipeline");
else
return _next(httpContext);
}
}
// Extension method used to add the middleware
// to the HTTP request pipeline.
public static class MySecondReportMiddlewareExtensions
{
public static IApplicationBuilder UseMySecondReportMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MySecondReportMiddleware>();
}
}
在Startup.cs
文件:
public void Configure(IApplicationBuilder app)
{
...
app.UseMySecondReportMiddleware();
...
}
将 HTTP 模块迁移到中间件
在这个食谱中,我们将学习如何转换一个 ASP。 asp.net HTTP 模块.NET Core 的中间件。
在开始之前,让我们温习一下什么是 HTTP 处理程序。 HTTP 模块可以为来自 ASP 的传入请求添加一个处理.NET/IIS 管道,在它到达处理程序之前,并/或向生成的响应添加处理。 它甚至可以转换或生成自己的响应。
准备
我们创建一个空的 ASP.NET Core web 应用与 Visual Studio 和。NET Core 或。NET Framework。
怎么做……
我们将创建一个新的 HTTP 模块,并跟踪请求的起始/结束点。 我们还将在响应的开始和结束处添加信息。
- 首先,让我们看看 HTTP 模块的结构:
public class MyHttpModule : IHttpModule
{
public void Dispose(){}
public void Init(HttpApplication context)
{
context.BeginRequest += (source, args) =>
{
context.Response.Write("MyHttpModule BeginRequest");
};
context.EndRequest += (source, args) =>
{
context.Response.Write("MyHttpModule EndRequest");
};
}
}
HttpModule 必须在Web.config
文件中配置如下:
<system.webServer>
<modules>
<add name="myModule" type="MyApp.HttpModules.MyHttpModule"/>
</modules>
</system.webServer>
- 现在,让我们看看在 ASP 中等效的代码.NET 的核心。 对该模块进行转换,使其与 asp.net 兼容.NET core,我们可以创建自定义中间件并调用它,或者创建内联中间件:
// Middleware class
public class MyModuleMiddleware
{
private readonly RequestDelegate _next;
public MyModuleMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync
("MyHttpModule BeginRequest");
await _next(httpContext);
await httpContext.Response.WriteAsync
("MyHttpModule EndRequest");
}
}
// Extension method used to add the middleware
// to the HTTP request pipeline.
public static class MyModuleMiddlewareExtensions
{
public static IApplicationBuilder UseMyModuleMiddleware
(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyModuleMiddleware>();
}
}
在Startup.cs
:
public void Configure(IApplicationBuilder app)
{
...
app.UseMyModuleMiddleware ();
...
}
public void Configure(IApplicationBuilder app)
{
...
app.Use(async (context, next) =>
{
// This code is equivalent to call the BeginRequest
// delegate in a HttpModule
await context.Response.WriteAsync
("MyHttpModule BeginRequest");
await next.Invoke();
// This code is equivalent to call the EndRequest
// delegate in a HttpModule
await context.Response.WriteAsync
("MyHttpModule EndRequest");
});
...
}
版权属于:月萌API www.moonapi.com,转载请注明出处