使用 ASP.NET Core 实现 Owin Pipeline






3.40/5 (4投票s)
如何使用 ASP.NET Core 实现 OWIN pipeline
引言
您可能知道,ASP.NET Core 工具(如 Thinktecture IdentityServer、OData、SinglarR 等)尚未实现。鉴于 ASP.NET Core 在 .NET 程序员中的流行度不断上升,并且如今许多软件都以此框架启动,因此在 ASP.NET Core 中使用上述功能的能力变得越来越重要。
使用 OData... ASP Core 最合理的方式是在 ASP Core pipeline 旁边实现 Owin pipeline,而不是作为替代品!
正如本文其余部分将要介绍的那样,我们将这两种架构一起使用。
目前,由于 OData、SignalR 等包尚未发布 .NetCore 版本,因此我们必须使用完整的 .NET Framework,当然,我们无法进行跨平台应用程序开发,也不能使用 Linux 等其他操作系统来实现应用程序。但一旦这些包发布到 .NetCore 框架版本,我们就可以放弃完整的 .NET,只使用 .NetCore。由于 owin 只是一个标准,而不是一个已实现的框架,所以在 ASP.NET Core 中不会有任何问题,因此我们可以轻松地将它们一起使用,并且将来可以实现跨平台应用程序,而无需对代码进行大量重构。
本例的实际应用是“Nancy”,它已发布 .NET Core 版本,我们可以在 Owin pipeline 上使用它,当然一切都很棒,您可以在 Scott Hanselman 的这篇文章中找到代码。
先决条件
本文的前提是熟悉 ASP.NET Core、中间件和 Owin 标准 pipeline,此外,您需要 Visual Studio 2015 更新 3 才能拥有 ASP.NET Core。
Using the Code
到目前为止,我发现这种方式的目标是能够使用与 Owin 兼容且已达到稳定版本的工具。例如,identity server 4 正在准备中,并且与 ASP Core 兼容,但它是一个 beta 版本,当然,它不稳定,最重要的是我们不确切知道最终发布时间!
创建一个 ASP.NET Core Web 应用程序的 C# 项目,完全使用 .NET Framework,并将其命名为“OwinCore
”。
在下一部分,选择“Empty”模板。
我将不再详细解释 ASP.NET Core,并假设您已经熟悉这个框架。
好的,现在打开“project.json”文件来处理此项目的依赖项。
在让您担心看到大量依赖项之前,我应该说明,使用 Owin 的主要重要程序包只有“Microsoft.Owin
”和“Microsoft.AspNetCore.Owin
”,其他程序包仅仅是为了展示这种方式的灵活性,以及我们可以在一个项目中拥有不同架构的不同程序包。
"dependencies": {
"Microsoft.AspNet.OData": "5.9.1",
"Microsoft.AspNet.SignalR": "2.2.1",
"Microsoft.AspNet.WebApi.Client": "5.2.3",
"Microsoft.AspNet.WebApi.Core": "5.2.3",
"Microsoft.AspNet.WebApi.Owin": "5.2.3",
"Microsoft.AspNetCore.Diagnostics": "1.0.0",
"Microsoft.AspNetCore.Hosting": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Owin": "1.0.0",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Net.Http": "2.2.29",
"Microsoft.Owin": "3.0.1",
"Microsoft.Owin.Diagnostics": "3.0.1",
"Microsoft.Owin.FileSystems": "3.0.1",
"Microsoft.Owin.StaticFiles": "3.0.1",
"Newtonsoft.Json": "9.0.1"
},
//etc...
保存此文件后,您可以在输出窗口中查看从 NuGet 包管理器下载的文件,您会看到 OData、SignalR、Owin,此外,我们还有 AspNetCore.Mvc
。这两种不同类型的程序包之所以能够协同工作并且不会出现任何问题。
在项目主目录下,创建一个名为“OwinExtensions
”的类,包含以下内容
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Owin.Builder;
using Microsoft.Owin.BuilderProperties;
using Owin;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace OwinCore
{
public static class OwinExtensions
{
public static IApplicationBuilder UseOwinApp(
this IApplicationBuilder aspNetCoreApp,
Action<IAppBuilder> configuration)
{
return aspNetCoreApp.UseOwin(setup => setup(next =>
{
AppBuilder owinAppBuilder = new AppBuilder();
IApplicationLifetime aspNetCoreLifetime =
(IApplicationLifetime)aspNetCoreApp.ApplicationServices.GetService
(typeof(IApplicationLifetime));
AppProperties owinAppProperties = new AppProperties(owinAppBuilder.Properties);
owinAppProperties.OnAppDisposing =
aspNetCoreLifetime?.ApplicationStopping ?? CancellationToken.None;
owinAppProperties.DefaultApp = next;
configuration(owinAppBuilder);
return owinAppBuilder.Build<Func<IDictionary<string, object>, Task>>();
}));
}
}
}
在 `IApplicationBuilder` 中添加了一个名为“UseOwinApp
”的扩展方法,该方法与 ASP.NET Core 相关,并且在该方法内部,创建了一个 `AppBuilder` 实例,该实例与 Owin pipeline 相关,从而使 Owin pipeline 可以与 ASP Core pipeline 并存。
现在我们想创建自定义 Owin 中间件并将其注册到应用程序的 Startup。创建一个名为“AddSampleHeaderToResponseHeadersOwinMiddleware
”的类,包含以下内容
using Microsoft.Owin;
using System.Threading.Tasks;
namespace OwinCore
{
public class AddSampleHeaderToResponseHeadersOwinMiddleware : OwinMiddleware
{
public AddSampleHeaderToResponseHeadersOwinMiddleware(OwinMiddleware next)
: base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
//throw new InvalidOperationException("ErrorTest");
context.Response.Headers.Add("Test", new[] { context.Request.Uri.ToString() });
await Next.Invoke(context);
}
}
}
这是一个继承自 `OwinMiddleware` 的类,并且 `Invoke` 是一个被重写的 `IOwinContext` 方法,在该方法中,我们可以实现自定义中间件。
我注释掉了 Exception,因为我们稍后会用到它,在下一行,在每个请求的响应头中,它会创建一个名为“Test
”的对象,其值为 Uri。
下一行表示您可以转到下一个中间件。
现在,打开“Startup.cs”,并像这样编辑“Configure
”方法
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env)
{
aspNetCoreApp.UseOwinApp(owinApp =>
{
if (env.IsDevelopment())
{
owinApp.UseErrorPage(new ErrorPageOptions()
{
ShowCookies = true,
ShowEnvironment = true,
ShowExceptionDetails = true,
ShowHeaders = true,
ShowQuery = true,
ShowSourceCode = true
});
}
owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>();
});
}
通过使用 `UseOwinApp`,我们可以注册 owin 中间件。值得注意的是,我们已经使用了 owin 中间件以及与 ASP NET Core pipeline 相关的 `IHostingEnviroment`。`owinApp.UseErrorPage` 已从 `Microsoft.Owin.Diagnostics` 中获取,在下一行,我们已注册了自定义 owin 中间件。
运行项目,您应该在任何请求的响应头中看到此内容
现在,如果我们取消注释自定义中间件中的 Exception 并再次运行,您应该会在开发环境中看到此页面。
然后注释掉该异常并继续...
为了展示 Owin 和 ASP.NET Core pipeline 可以轻松地并存,我们创建了一个新的自定义 ASP.NET Core 中间件,并将其注册到“Configure
”方法中。
创建一个名为“AddSampleHeaderToResponseHeadersAspNetCoreMiddlware
”的新类,包含以下内容
namespace OwinCore
{
public class AddSampleHeaderToResponseHeadersAspNetCoreMiddlware
{
private readonly RequestDelegate Next;
public AddSampleHeaderToResponseHeadersAspNetCoreMiddlware(RequestDelegate next)
{
Next = next;
}
public async Task Invoke(HttpContext context)
{
//throw new InvalidOperationException("ErrorTest");
context.Response.Headers.Add("Test2", new[] { "some text" });
await Next.Invoke(context);
}
}
}
在 `Startup` 类的 `Configure` 方法中,我们有这个 `changeset`
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env)
{
aspNetCoreApp.UseOwinApp(owinApp =>
{
if (env.IsDevelopment())
{
owinApp.UseErrorPage(new ErrorPageOptions()
{
ShowCookies = true,
ShowEnvironment = true,
ShowExceptionDetails = true,
ShowHeaders = true,
ShowQuery = true,
ShowSourceCode = true
});
}
owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>();
});
aspNetCoreApp.UseMiddleware<AddSampleHeaderToResponseHeadersAspNetCoreMiddlware>();
}
现在,“AddSampleHeaderToResponseHeadersAspNetCoreMiddlware
”中间件已注册,您可以在任何请求的响应头中看到结果。
运行项目后,您应该会看到类似以下的内容
您可以看到来自不同 pipeline 的中间件正在协同工作,并且由于 Owin 中间件在 ASP.NET Core 中间件之前注册,因此它们将按照注册中间件的顺序执行。
“Test
”由 owin 创建,“Test2
”由 ASP.NET Core 创建。
现在我想在 ASP NET Core 应用程序中使用 owin pipeline 来实现 OData 协议。
首先,创建一个名为“Product
”的类,如下所示
namespace OwinCore
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
现在,我们创建一个名为“ProductsController
”的类,包含以下内容
namespace OwinCore
{
public class ProductsController : ODataController
{
[EnableQuery]
public IQueryable<Product> Get()
{
return new List<Product>
{
new Product { Id = 1, Name = "Test" , Price = 10 }
}
.AsQueryable();
}
}
}
如果您熟悉 OData
协议,您就会知道这个控制器是如何工作的,但如果不熟悉,它就是一个使用 web API 的简单 OData 控制器,`Get` 函数返回一个 Product
的 `IQueryable`,我们可以使用 JayData 或 Breeze 等第三方工具对请求的查询进行过滤。
很明显,OData
是与 entity framework 相关的 nuget 包!
OData 配置非常简单,您可以在 Configure
方法中这样操作
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env)
{
//aspNetCoreApp.UseMvc();
aspNetCoreApp.UseOwinApp(owinApp =>
{
if (env.IsDevelopment())
{
owinApp.UseErrorPage(new ErrorPageOptions()
{
ShowCookies = true,
ShowEnvironment = true,
ShowExceptionDetails = true,
ShowHeaders = true,
ShowQuery = true,
ShowSourceCode = true
});
}
// owinApp.UseFileServer(); as like as asp.net core file server middleware
// owinApp.UseStaticFiles(); as like as asp.net core static files middleware
// owinApp.UseWebApi(); asp.net web api / odata / web hooks
//OData config
HttpConfiguration webApiConfig = new HttpConfiguration();
ODataModelBuilder odataMetadataBuilder = new ODataConventionModelBuilder();
odataMetadataBuilder.EntitySet<Product>("Products");
webApiConfig.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model: odataMetadataBuilder.GetEdmModel());
owinApp.UseWebApi(webApiConfig);
//End of OData config
owinApp.MapSignalR();
//owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>();
});
//aspNetCoreApp.UseMiddleware<AddSampleHeaderToResponseHeadersAspNetCoreMiddlware>();
OData 需要一个 `HttpConfiguration` 实例,使用 `ODataModelBuilder` 我们可以添加所有需要的 `EntitySet`,并使用 `MapODataServiceRoute` 来处理路由,如果我们有多个 `OData` 端点,我们可以轻松地使用不同的 `routeName` 和 `routePrefix` 进行多个路由。
通过实现 owin pipeline,我们可以像普通的 ASP.NET 项目一样轻松地配置 OData,您可以看到您也可以配置 SignalR 和其他程序包。
运行项目并发送此 get 请求
https://:YourPort/odata/Product
此请求的响应应类似于下面的对象
{
"@odata.context":"https://:4675/odata/$metadata#Products","value":[
{
"Id":1,"Name":"Test","Price":10
}
]
}
通过此教程,可以在 ASP NET Core 应用程序中轻松实现许多 owin 中间件,例如“Thinktecture IdentityServer”、“NWebSec”、“Facebook OAuth”等,只需使用它们的 owin 配置即可。
在 .NET Core 版本中准备好这些程序包后,我们就可以轻松使用它们,而无需进行大规模的代码重构!
结论
ASP.NET Core 在实现高性能和强大的 Web 应用程序方面令人惊叹。此时,我们应该对其进行自定义,以拥有 Microsoft 的所有出色功能。
如果您有任何问题,可以轻松按照 Github 上的说明进行操作,并拥有丰富的提交注释来理解本文,您还可以找到 Autofac 作为 IOC 容器的实现以及 SignalR。我很快会写一篇文章来介绍这些功能。
历史
- 2016年9月8日:初始版本