bit.ly/MY7GzO)。

现代 Web 应用程序必须能够快速地发展,充分利用许多不同的组件和框架。它们的占用空间必须很小,这样才能在云的大型运行时环境中有效地运行。

确保 ASP.NET 能够满足这些当前需求和未来需求正是 Katana 项目的主要目标。

Katana 简介

由于这一规范的目的是发展一个广阔且充满活力的、基于 Microsoft .NET Framework 的 Web 服务器和应用程序组件生态系统,因此它可以将服务器与应用程序之间的交互减少到一小部分类型和单个函数签名,这个函数签名被称为应用程序委托(即 AppFunc):

  1.  
  2.           using AppFunc = Func<IDictionary<string, object>, Task>;
  3.         

为了更有效地使用资源,管道中的所有组件都应该是异步的,这体现在返回 Task 对象的应用程序委托中。

虽然任何键/值数据都可以插入到环境字典中,但 OWIN 规范为某些 HTTP 核心元素定义了键,如图 1 中所示。

图 1 HTTP 请求的必需环境字典键

 

键名称 值说明
一个带有请求正文(如果有)的流。如果没有请求正文,Stream.Null 可以用作占位符。
请求标头的 IDictionary<string, string[]>。
一个包含请求的 HTTP 请求方法的字符串(例如 GET 和 POST)。
此路径必须是应用程序委托的“根”的相对路径。
一个字符串,包含对应于应用程序委托的“根”的请求路径部分。
一个包含协议名称和版本的字符串(例如 HTTP/1.0 或 HTTP/1.1)。
该值可以是空字符串。
一个字符串,包含用于请求的 URI 方案(例如 HTTP 或 HTTPS)。

定义一组基本的环境字典键/值对,使得许多不同的框架和组件作者可以在一个 OWIN 管道中进行互操作,而不必强制实施对特定 .NET 对象模型的协议,例如针对 ASP.NET MVC 中的 HttpContextBase 或 ASP.NET Web API 中的 HttpRequestMessage/HttpResponseMessage 的协议。

Katana 项目是 Microsoft 创建和推出的基于 OWIN 的组件和框架集合。

Katana 组件可以通过体系结构堆栈查看,如图 2 中所示。

转  Katana 项目入门 
图 2 Katana 项目体系结构

此堆栈由以下层组成:

  • 主机负责启动、加载其他 OWIN 组件和正常关闭。
  • 服务器:负责绑定到 TCP 端口,构造环境字典和通过 OWIN 管道处理请求。
  • 它可以是从简单压缩组件到 ASP.NET Web API 这样的完整框架,不过从服务器的角度而言,它只是一个公开应用程序委托的组件。
  • 事实上,对于这些类型的应用程序,Katana 组件只需使用一个小的配置类即可见。

在处理 HTTP 请求时,各个层一起工作,方式类似于图 3 中所示的数据流。

转  Katana 项目入门 
图 3 Katana 中数据流的示例

使用 Katana 生成现代 Web 应用程序

现代 Web 应用程序通常具有四项功能:

  1. 服务器端标记生成
  2. 静态文件服务
  3. 用于处理 AJAX 请求的 Web API
  4. 实时消息

不过,从这些框架编写应用程序颇具挑战性,当前要求在 IIS 上托管不同的应用程序部分,可能需要使用应用程序和虚拟目录将这些部分相互隔离。

这样可提供多种优势:

  • 部署过程非常简单,因为只涉及一个应用程序,而不是每种功能一个应用程序。
  • 可以添加验证之类的额外功能,这些功能可应用于管道中的所有下游组件。
  • 不同的组件,无论是 Microsoft 还是第三方组件,都可以通过环境字典对同一个请求状态执行操作。

signalr.net) 用于实时消息服务。

另外,我不打算花费很多时间在浏览器客户端的标记和脚本上,我将使用 Knockout.js 来将 HTML 标记与 Web API 及 SignalR 数据分隔开。

需要牢记的首要原则是,我正在将所有这些不同的框架编写到一个 OWIN 管道中,这样当新功能可用时,我只需将这些功能插入到管道,即可将它们添加到应用程序中。

开始使用

牢记这一点后,我将从在 Visual Studio 2013 Preview 中创建一个空的新 ASP.NET Web 应用程序项目开始,如图 4 中所示。

转  Katana 项目入门 
图 4 Visual Studio 2013 Preview 中的新 ASP.NET Web 应用程序项目

您可以将基于 Katana 的应用程序创建为类库,但您需要修改项目属性以便符合此结构,或者提供自己的自定义应用程序加载程序,该加载程序能够在其他文件夹结构中搜索程序集和类型。

下一步,我将使用 Nancy Web 框架生成服务器端标记生成代码。

诸如 ASP.NET MVC 之类的框架与 System.Web.dll 具有依赖关系(截止编写本文时),这使得它们不太适合用于非 IIS 托管方案。

docs.nuget.org 上详细了解 NuGet。)在编写本文时,此处使用的许多程序包都是预发布版本,因此请确保可在 NuGet 对话框中显示预发布程序包。

这会将 Nancy 程序包作为依赖关系安装,并且提供额外的辅助方法在我的 OWIN 管道中配置 Nancy。

下面是模块 (HomeModule.cs) 的代码:

  1.  
  2.           public class HomeModule : NancyModule
  3. {
  4.   public HomeModule() {
  5.     Get["/"] = _ => {
  6.       var model = new { title = "We've Got Issues..." };
  7.       return View["home", model];
  8.     };
  9.   }
  10. }
  11.         

图 5 中所示,该视图会将模型的标题属性同时插入到页标题和 h1 元素中。

图 5 Home.html

  1.  
  2.           <!DOCTYPE html>
  3. <html >
  4. <head>
  5.   <title>@Model.title</title>
  6. </head>
  7.   <body>
  8.     <header>
  9.       <h1>@Model.title</h1>   
  10.     </header>
  11.     <section>
  12.       <h2>Backlog</h2>
  13.       <ul class="bugs" id="backlog">
  14.         <li>a bug</li>
  15.       </ul>
  16.     </section>
  17.     <section>
  18.       <h2>Working</h2>
  19.       <ul class="bugs" id="working">
  20.         <li>a bug</li>
  21.       </ul>
  22.     </section>
  23.     <section>
  24.       <h2>Done</h2>
  25.       <ul class="bugs" id="done">
  26.         <li>a bug</li>
  27.       </ul>
  28.     </section>
  29.   </body>
  30. </html>
  31.         

有关这些列表的更多信息,请参阅 Nancy 文档。

为此,我首先需要安装 Katana 主机和服务器组件,然后编写少量的探测代码来设置 OWIN 管道并将 Nancy 插入该管道中。

bit.ly/19EZ2Rw)。

当 Katana 主机加载 OWIN 应用程序时,它会根据以下规则发现并运行启动类(按照优先级顺序):

  • 该值必须是有效的 .NET 类型名称。
  • 如果程序集包含属性 [assembly:OwinStartup(typeof(MyStartup))],则加载程序将使用属性值中指定的类型。
  • 如果这些条件中的任意一个为真,加载程序将通过查找名为 Startup 的类型(带有与签名 void Configure(IAppBuilder app) 匹配的方法)来扫描已加载的程序集。

不过,如果您的项目中有许多不同的类型和程序集,那么使用 appSetting 或程序集属性来防止不必要的扫描可能会更好。

我创建一个名为 Startup 的新类,并按下面所示添加配置方法:

  1.  
  2.           public class Startup
  3. {
  4.   public void Configuration(IAppBuilder app)
  5.   {
  6.     app.UseNancy();
  7.   }
  8. }
  9.         

虽然您可以使用 IAppBuilder 的更通用的 Use 方法来添加中间件,不过许多中间件库将提供这些有用的扩展方法,这些方法可以简化配置过程。

此时,OWIN 管道包含单个组件,即 Nancy,如图 6 中所示。

转  Katana 项目入门 
图 6 包含单个组件的正常运行的 Web 应用程序

将数据与 ASP.NET Web API 合并

接下来,浏览器会加载 HTML 页并立即执行 JavaScript,后者将从 Web API 中提取数据并在页自身中动态生成 HTML 标记。

在安装框架后,我将创建一个简单的 API,如图 7 中所示。

图 7 BugsController.cs

  1.  
  2.           public class BugsController : ApiController
  3. {
  4.   IBugsRepository _bugsRepository = new BugsRepository();
  5.   public IEnumerable<Bug> Get()
  6.   {
  7.     return _bugsRepository.GetBugs();
  8.   }
  9.   [HttpPost("api/bugs/backlog")]
  10.   public Bug MoveToBacklog([FromBody] int id)
  11.   {
  12.     var bug = _bugsRepository.GetBugs().First(b=>b.id==id);
  13.     bug.state = "backlog";
  14.     return bug;
  15.   }
  16.   [HttpPost("api/bugs/working")]
  17.   public Bug MoveToWorking([FromBody] int id)
  18.   {
  19.     var bug = _bugsRepository.GetBugs().First(b => b.id == id);
  20.     bug.state = "working";
  21.     return bug;
  22.   }
  23.   [HttpPost("api/bugs/done")]
  24.   public Bug MoveToDone([FromBody] int id)
  25.   {
  26.     var bug = _bugsRepository.GetBugs().First(b => b.id == id);
  27.     bug.state = "done";
  28.     return bug;
  29.   }
  30. }
  31.         

asp.net/web-api。

为此,我只需将下面的代码行添加到启动类的配置方法中:

  1.  
  2.           var config = new HttpConfiguration();
  3. config.MapHttpAttributeRoutes();
  4. config.Routes.MapHttpRoute("bugs", "api/{Controller}");
  5. app.UseWebApi(config);
  6.         

现在,OWIN 管道包含两个组件,即 ASP.NET Web API 和 Nancy,如图 8 中所示。

转  Katana 项目入门 
图 8 包含两个组件的 OWIN 管道

如果没有任何管道组件可以处理特定的请求,默认的 Katana 组件将返回 HTTP 404 响应。

knockoutjs.com。

图 9 所示。

图 9 设置错误 viewModel

  1.  
  2.           <script>
  3.   $(function () {
  4.     var viewModel;
  5.     $.getJSON('/api/bugs', function(data) {
  6.       var model = data;
  7.       viewModel = {
  8.         backlog: ko.observableArray(
  9.           model.filter(function(element) return element.state === 'backlog'; })),
  10.         working: ko.observableArray(
  11.           model.filter(function(element) return element.state === 'working'; })),
  12.         done: ko.observableArray(
  13.           model.filter(function(element) return element.state === 'done'; })),
  14.         changeState: function (bug, newState) {
  15.           var self = this;
  16.           $.post('/api/bugs/' + newState, '': bug.id }, function(data){
  17.             self.moveBug(data);
  18.           });
  19.         },
  20.         moveBug: function (bug) {
  21.           // Remove the item from one of the existing lists
  22.           ...
  23.           // Add bug to correct list
  24.           this[bug.state].push(bug);
  25.         }
  26.       };
  27.       ko.applyBindings(viewModel);
  28.     })
  29.   })
  30. </script>
  31.         

例如,积压列表可以从 viewModel 使用图 10 中所示的属性生成。

图 10 用于生成积压列表的属性

    1.  
    2.           <section>
    3.   <h2>Backlog</h2>
    4.   <ul class="bugs" id="backlog" data-bind="foreach:backlog">
    5.     <li>
    6.       [<span data-bind="text: id"></span>] <span data-bind="text: title"></span>:
    7.         <span data-bind="text: description"></span>
    8.       <ul>
    9.         <li><a href="#" data-bind
    10. ="click: $root.changeState.bind($root, $data,
    11.           'working')"
>Move to working</a></li>   
  •         <li><a href="#" data-bind
  • ="click: $root.changeState.bind($root, $data,
  •           'done')"
>Move to done</a></li>   
  •       </ul>
  •     </li>
  •   </ul>
  • </section>
  •         

使用 SignalR 添加实时更改通知

此外,当前功能级别的基础技术 Nancy 和 ASP.NET Web API 在相同的 OWIN 管道中一起运行。

编写 SignalR 的目的也是为了在 OWIN 管道中运行,因此将它添加到现有的应用程序中不过是小事一桩。

bit.ly/14WIx1t。)在我的应用程序中,当 ASP.NET Web API 接收到更改错误状态的请求时,它将会更新错误,然后通过 SignalR Hub 将更新后的错误广播给当前连接到应用程序的所有浏览器客户端。

因为我不会利用任何其他 SignalR 功能,因此我的 Hub 将只包含以下空的类定义:

  1.  
  2.           [HubName("bugs")]
  3. public class BugHub : Hub
  4. {
  5. }
  6.         

我可以通过添加以下 BugsController 构造函数来实现此目的:

  1.  
  2.           public BugsController()
  3. {
  4.   _hub = GlobalHost.ConnectionManager.GetHubContext<BugHub>();
  5. }
  6.         

从其中一个 MoveToXX 操作,我接下来可以将更新的错误广播到所有连接的浏览器客户端:

  1.  
  2.           _hub.Clients.All.moved(bug);
  3.         

在主视图中,将几个脚本引用添加到 SignalR JavaScript 库后,我可以使用以下代码连接到 bugsHub 并开始侦听“已变换”消息:

  1.  
  2.           $.connection.hub.logging = true;
  3. var bugsHub = $.connection.bugs;
  4. bugsHub.client.moved = function (item) {
  5.   viewModel.moveBug(item);
  6. };
  7. $.connection.hub.start().done(function() {
  8.   console.log('hub connection open');
  9. });
  10.         

通过打开两个浏览器窗口,在其中一个窗口中进行更改,然后在另一个窗口中查看状态更改,即可清楚地了解这一点。

我只需将下面的代码添加到启动类的配置方法中:

  1.  
  2.           app.MapSignalR();
  3.         

这将创建类似图 11 中所示的管道。

转  Katana 项目入门 
图 11 包含三个组件的 OWIN 管道

转向自托管

因此,除了提供更加简单的、代码驱动的方法来编写管道组件之外,我真正实现了哪些功能?

不过,所有这些层都可以轻松地替换,包括服务器和主机。

自托管在许多方案中是非常有用的,从开发计算机上未安装 Web 服务器的安装情形,到在使用进程隔离并且不公开 Web 服务器访问的共享托管环境中部署应用程序的生产情形。

我将从安装以下附加 NuGet 程序包开始:

  • bit.ly/153aIca)
  • bit.ly/162Uzj8)

唯一的要求是,在运行时,/bin 文件夹中必须存在这些文件,而重新生成是一种将文件复制到 /bin 的便捷方式。

在安装程序包和复制文件之后,我打开命令提示符,导航到 Web 项目的根文件夹,然后从程序包文件夹中调用 OwinHost.exe,如图 12 中所示:


          > ..
          \packages\OwinHost.2.0.0\tools\OwinHost.exe
        

转  Katana 项目入门 
图 12 从程序包文件夹中调用 OwinHost.exe

接下来,我可以导航到 http://localhost:5000,确认整个应用程序正在运行。

并且,由于堆栈的所有组件之间的约定只是应用程序委托,创新的步伐可以远远超越目前提供的功能。

这仅仅是一个开端

新版本有两个值得关注的方面:

  • 为自托管提供核心基础结构组件
  • 提供了一套丰富的验证中间件(包括 Facebook、Google、Twitter 和 Microsoft Account 这样的社交提供商)以及适用于 Windows Azure Active Directory、cookie 和联合身份验证的提供程序

bit.ly/1alOF4m 中。

相关文章:

  • 2022-12-23
  • 2021-10-16
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-12-27
  • 2021-12-09
猜你喜欢
  • 2022-02-07
  • 2021-11-13
  • 2021-04-07
  • 2021-10-26
  • 2021-12-21
  • 2021-07-20
  • 2022-12-23
相关资源
相似解决方案