ASP.NET Core:简洁的力量

阅读数:1250 2018 年 5 月 29 日

关键要点

  • 在构建 Web 应用程序时,OWIN 为开发人员提供了一个很好的 Web 服务器抽象。
  • ASP.NET Core 脱离了标准 OWIN 实现,这是为开发人员而做出的简化。
  • 通过抽象来简化复杂的事物并不会削弱它,只是让它变得更简单易用。
  • 通过使用中间件而不是高级框架,开发人员可以构建出既简洁又强大的应用程序。
  • 即使没有在使用 ASP.NET Core,也可以通过在旧的解决方案中使用 Katana 来获得相关功能。

随着.NET Core 2.0 的发布,这个最初于 2016 年发布的具有通用性、模块化、跨平台和开放源代码的平台终于有了最新的主要版本。.Net Core 包含了很多在当前版本的.NET Framework 中可用的 API。它的初衷是为下一代 ASP.NET 解决方案提供支持,但现在却成为了其他场景的基础,包括物联网、云计算和下一代移动解决方案。在本系列文章中,我们将探讨.NET Core 的一些优势,以及它如何给传统.NET 开发者以及所有需要为市场提供强大、高性能和经济解决方案的技术人员带来好处。

这篇是“.NET Core”系列文章的一部分。读者可以通过订阅RSS接收更新通知。

在微软决定重新构想他们的 Web 开发平台 ASP.NET 时,他们认为将其与 IIS 绑定可能不是一个好主意。原来的 ASP.NET 构建于 IIS 的特定技术之上,这样不仅导致 ASP.NET 与 Windows 绑定在一起,而且无法进行自行托管。这些限制导致它很难切入到以云为中心的世界。微软决定全面开放 Open Web Interface for .NET(或者叫作 OWIN),并且将 Web 服务器全面抽象出来。框架以及框架的用户因此可以完全忽略哪个服务器负责处理哪个 HTTP 请求,他们可以专注于构建所需的功能。

不过,OWIN 并不是一个新概念。OWIN 规范已经存在好几年了,微软早就允许开发人员通过一个名为 Katana 的开源项目在 IIS 中使用 OWIN。事实上,微软不仅允许开发人员通过 Katana 使用 OWIN,几年来,OWIN 已经成为所有 ASP.NET 身份验证功能的基础。

那么,OWIN 究竟是什么?说实话,它是个很简单的东西!而正是这种简单性让它变得如此伟大。它是一个接口,只使用预定义的代理和一个由字符串和对象组成的通用字典来抽象 Web 服务器。因此,它没有使用基于事件驱动的架构,而是定义了一个管道,叫作中间件(middleware)。

中间件其实是一段代码,负责与来自客户端的请求发生交互。(是的,我故意用“一段代码”这个词来说明它的通用性)。它可以通过几种不同的方式来实现。虽然基于类的方式在大多数情况下是首选,但其实一个具有正确签名的代理就足够了。中间件代码可以检查或者修改从客户端到达的请求和返回给客户端的响应。如果有必要,它甚至可以生成发送给客户端的响应。

然后中间件组成了一个管道,传入的消息按照中间件加入管道的顺序从一个中间件传到另一个中间件,直到其中一个中间件知道如何生成响应消息为止。然后响应消息被传回管道进行检查,在发送给客户端之前,中间件有可能会修改它。原理非常简单,但仍然非常灵活和强大。

这个过程被称为请求管道,消息流经管道,中间件在它流过时与它发生交互。但是,管道需要某种形式的管道对象来负责将消息在中间件之间传递。在 OWIN 中,中间件实际上更像是一个链表。传入的请求被传递给列表中的第一个中间件,在完成处理后再传递给下一个中间件。该请求从一个中间件传递到另一个中间件,直到其中的一个产生响应消息并将响应消息发回到该列表,让之前的中间件检查响应消息。事实上,这个过程有点复杂,因为响应消息实际上应该在生成后立即发送给客户端,甚至是在将它回传给管道之前。这使得修改响应消息变得稍微复杂一点,但为了简单起见,我们就忽略这一部分。

OWIN 规范将传入的请求定义为由字符串和对象组成的字典。服务器接收传入的 HTTP 请求,将请求信息拆分为可用的块,并将其放入字典中,然后将其传递给第一个中间件进行处理。它使用预定义的关键字将请求路径、头部信息集合、包含消息体的流等内容添加到字典中。中间件使用这些关键字从字典中读取所需的信息,并完成相应的处理。除此之外,服务器还会添加用于生成响应消息的对象到字典中,如响应流和响应头部信息集合,中间件就可以通过使用字典中的对象来生成响应并发送给客户端。最重要的是,服务器实际上可以在字典中加入代理,以便向中间件提供额外的功能。例如,有一个 OWIN 规范扩展允许服务器提供一种专门的方式将文件发送给客户端。服务器通过提供一个预定义的代理放到字典中,并暴露给中间件。然后,中间件可以查询字典以查看当前服务器是否支持该功能,如果是,就调用代理将将文件发给客户端。

OWIN 接口非常简单,但也很强大。它为我们提供对 HTTP 请求和响应的底层访问,同时又抽象出处理实际请求的繁琐细节。不过,它并不会限制我们通过代理来提供更高级别抽象的能力。另一方面,因为太过底层,有时候使用起来确实有点麻烦,我想这就是为什么微软决定以自己的方式拥抱 OWIN。

微软在将 OWIN 管道添加到 ASP.NET Core 时,对其进行了一些更改。或者说,他们在上面添加了一个层,使其更容易操作(至少我看到的就是这样)。他们不是仅仅为中间件提供了一个通用字典,而是在其上添加了一个类型层,使其更易于使用。因此,我们不是要去获取字典,而是获取一个 HttpContext 对象,这样我们就能够以更简单的方式与请求和响应发生交互。例如,我们可以使用 httpContext.Response.Body.Write(...) 向客户端返回消息,而不是使用 ((Stream)dictionary[“owin.RequestBody”]).Write(...)。虽然差别不是很大,但起码不需要去记住关键字是什么,也不需要做类型转换,这样就不容易出错。

在 Katana 项目中,HttpContext 对字典进行了包装,这样我们就可以以类型的方式来访问其中的数据项。另一方面,在 ASP.NET Core 中,它是一个完全自定义的对象。不过,如果我们想要使用基于 OWIN 的中间件,可以使用一些可用的扩展来提供“适当的”OWIN API。

那么,我们是否真的要使用这样的低级 API 来构建应用程序?当然不是!在这个低级 API 之上构建更高级的抽象是相当容易的,这些恰恰是微软已经完成的事情。在 ASP.NET Core 中,ASP.NET MVC 就是一个中间件实现。它包含了几个使用.NET Core 依赖注入功能注入到中间件的服务,但它本身是一个中间件,负责与客户端的请求发生交互。这就是这个底层 API 发挥其威力的地方。

因为我们可以在请求管道中注册任意多的中间件,所以可以加入我们想要使用的框架和功能。想要使用 ASP.NET MVC 来生成 UI?不用担心,只需添加 MVC 中间件和支持服务即可。想要在 API 中使用 NancyFx?没问题,只需将它添加到请求管道中。想使用 Facebook 身份验证?没问题!只需添加所需的中间件和服务即可。想要将 Twitter 身份验证与 JWT 令牌结合起来用在你的 API 中?添加中间件即可。我确定你已经明白了……这个简单的接口让我们能够加入我们需要的东西,将第三方中间件与我们自己定制的中间件相结合,创建一个完全自定义的请求管道,以满足我们的需求。最重要的是,如果没有处理请求的中间件,就什么都不会发生。这与在 IIS 中托管 ASP.NET 非常不同,其中一些功能来自 ASP.NET,有些来自 IIS。

我没有为 ASP.NET Core 请求管道写过一篇完整的文章,也没有代码来展示它的工作原理。所以,我们就以身份验证为例。这是了解 OWIN 最常见的方式之一,同时也是展示这个接口强大功能的一个很好的例子,所以在这里使用它似乎很合适。

将身份验证添加到 ASP.NET Core Web 应用程序意味着需要将一些东西添加到应用程序中。首先,我们需要将认证服务添加到服务集合中,然后告诉它我们想要支持的不同类型的认证。

假设我们想要使用 Facebook 身份认证,并且在用户通过身份验证后,我们希望使用 Cookie 认证为他们授权。配置看起来像这样:

复制代码
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(CookieAuthenticationDefautls.ApplicationScheme) .AddFacebook(“### CLIENT ID ###”, “### CLIENT SECRET ###”)
.AddCookie();
}

一旦有了这些服务,就可以像这样将认证中间件添加到请求管道中:

复制代码
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
// Middlewares that don’t require authenticated users
app.UseAuthentication();
// Middlewares that might require authenticated users
}

最后,当我们想要将用户重定向到 Facebook 进行身份验证时,只需要调用:

复制代码
await HttpContext.ChallengeAsync(
new AuthenticationProperties { RedirectUri = "/userinfo" },
FacebookDefaults.AuthenticationScheme);
}

此调用将使用已定义的身份验证方案来设置 HttpContext,将用户重定向到正确的登录页面。在这种情况下,Facebook 身份验证处理程序会将返回的状态代码设置为 HTTP 302,并且将 location 头部字段设置为 Facebook 的认证地址,以便将客户端重定向到该地址。一旦用户通过 Facebook 的身份验证,并被重定向回带有身份令牌的应用程序,身份验证中间件就会捕获该回调并使用 Facebook 服务验证令牌。如果一切正常,它会要求 Cookie 身份验证服务发出一个用于保持客户端登录的 Cookie。实际上,这意味着 Cookie 身份验证服务会将一个头部信息添加到 HttpContext 的响应头部字典中,然后再添加到响应消息中。

我们通过使用几个扩展方法来注册一些服务和一个中间件,建立了一个相当复杂的认证流程,在请求认证时重定向用户,并在认证发生时处理来自身份提供者的回调。

注意:在以前的配置中,它实际上会尝试使用 Cookie 身份验证来验证用户身份,而不是默认使用 Facebook 身份认证。但只需要作出一个很小的配置更改,就可以将 Facebook 作为默认的认证机制,但这已经超出了本文讨论的范围。

我希望你也赞同我所说的,迁移到基于 OWIN 的管道是一件好事。它可能是一个非常简单的底层接口,但如果使用得当,它会发挥巨大的威力。创建一个简单易用的接口,可以兼顾底层和高级的用法,并且不会对未来造成限制,这不是一件容易的事。但我确信这个接口能做到。现在,我们需要充分利用它!只要你稍微有点创意,就没有什么可以限制你——你只需要搞清楚需要解决什么问题。

关于作者

Chris Klug 是瑞典斯德哥尔摩 tretton37 的一名软件开发人员和架构师。他一生中花费了很多时间通过编写软件来解决问题,他喜欢编码时的创造性以及在编码过程中不断遇到的挑战。他还花费大量时间在世界各地的开发者会议上发表演讲,这显然引起了微软的关注,并授予他 MVP 嘉奖。但是当被问及兴趣爱好时,他承认,相比玩电脑,他更喜欢在一个漂亮的海滩上放风筝,或者继续扩大他身上的纹身。

随着.NET Core 2.0 的发布,这个最初于 2016 年发布的具有通用性、模块化、跨平台和开放源代码的平台终于有了最新的主要版本。.Net Core 包含了很多在当前版本的.NET Framework 中可用的 API。它的初衷是为下一代 ASP.NET 解决方案提供支持,但现在却成为了其他场景的基础,包括物联网、云计算和下一代移动解决方案。在本系列文章中,我们将探讨.NET Core 的一些优势,以及它如何给传统.NET 开发者以及所有需要为市场提供强大、高性能和经济解决方案的技术人员带来好处。

这篇是“.NET Core”系列文章的一部分。读者可以通过订阅 RSS 接收更新通知。

查看英文原文ASP.NET Core - The Power of Simplicity

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论