ASP.NET Core框架深度学习(二) 管道对象
4、HttpContext 第一个对象
我们的ASP.NET Core Mini由7个核心对象构建而成。第一个就是大家非常熟悉的HttpContext对象,它可以说是ASP.NET Core应用开发中使用频率最高的对象。要说明HttpContext的本质,还得从请求处理管道的层面来讲。对于由一个服务器和多个中间件构建的管道来说,面向传输层的服务器负责请求的监听、接收和最终的响应,当它接收到客户端发送的请求后,需要将它分发给后续中间件进行处理。对于某个中间件来说,当我们完成了自身的请求处理任务之后,在大部分情况下也需要将请求分发给后续的中间件。请求在服务器与中间件之间,以及在中间件之间的分发是通过共享上下文的方式实现的。

如上图所示,当服务器接收到请求之后,会创建一个通过HttpContext表示的上下文对象,所有中间件都是在这个上下文中处理请求的,那么一个HttpContext对象究竟携带怎样的上下文信息呢?我们知道一个HTTP事务(Transaction)具有非常清晰的界定,即接收请求、发送响应,所以请求和响应是两个基本的要素,也是HttpContext承载的最核心的上下文信息。
我们可以将请求理解为输入、响应理解为输出,所以应用程序可以利用HttpContext得到当前请求所有的输入信息,也可以利用它完成我们所需的所有输出工作。为此我们为ASP.NET Core Mini定义了如下这个极简版本的HttpContext。
public class HttpContext
{
public HttpRequest Request { get; }
public HttpResponse Response { get; }
}
public class HttpRequest
{
public Uri Url { get; }
public NameValueCollection Headers { get; }
public Stream Body { get; }
}
public class HttpResponse
{
public NameValueCollection Headers { get; }
public Stream Body { get; }
public int StatusCode { get; set;}
}
如上面的代码片段所示,HttpContext通过它的两个属性Request和Response来表示请求和响应,它们对应的类型分别为HttpRequest和HttpResponse。通过前者,我们可以得到请求的地址、手部集合和主体内容,利用后者,我们可以设置响应状态码,也可以设置首部和主体内容。
5、RequestDelegate 第二个对象
RequestDelegate是我们介绍的第二个核心对象。我们从命名可以看出这是一个委托(Delegate)对象,和上面介绍的HttpContext一样,我们也只有从管道的角度才能充分理解这个委托对象的本质。
软件的架构设计有这样的认识:好的设计一定是“简单”的设计。所以每当我在设计某个开发框架的时候,一直会不断告诉我自己:“还能再简单点吗?”。我们上面介绍的ASP.NET Core管道的设计就具有“简单”的特质:Pipeline = Server + Middlewares。但是“还能再简单点吗?”,其实是可以的:我们可以将多个Middleware构建成一个单一的“HttpHandler”,那么整个ASP.NET Core框架将具有更加简单的表达:Pipeline =Server + HttpHandler。

那么我们如来表达HttpHandler呢?我们可以这样想:既然针对当前请求的所有输入和输出都通过HttpContext来表示,那么HttpHandler就可以表示成一个Action<HttpContext>对象。那么HttpHandler在ASP.NET Core中是通过Action<HttpContext>来表示的吗?其实不是的,原因很简单:Action<HttpContext>只能表示针对请求的 “同步” 处理操作(void),但是针对HTTP请求既可以是同步的,也可以是异步的,更多地其实是异步的。
那么在.NET Core的世界中如何来表示一个同步或者异步操作呢?你应该想得到,那就是Task对象,那么HttpHandler自然就可以表示为一个Func<HttpContext,Task>对象。由于这个委托对象实在太重要了,所以我们将它定义成一个独立的类型。

public delegate Task RequestDelegate(HttpContext context);
其实跟Func<HttpContext,Task>是一样的
6、Middleware 第三个对象
在对RequestDelegate这个委托对象具有充分认识之后,我们来聊聊中间件又如何表达,这也是我们介绍的第三个核心对象。中间件在ASP.NET Core被表示成一个Func<RequestDelegate, RequestDelegate>对象,也就是说它的输入和输出都是一个RequestDelegate。

对于为什么会采用一个Func<RequestDelegate, RequestDelegate>对象来表示中间件,很多初学者会很难理解。我们可以这样的考虑:对于管道的中的某一个中间件来说,由后续中间件组成的管道体现为一个RequestDelegate对象,由于当前中间件在完成了自身的请求处理任务之后,往往需要将请求分发给后续中间件进行处理,所以它需要将由后续中间件构成的RequestDelegate作为输入。
当代表中间件的委托对象执行之后,我们希望的是将当前中间件“纳入”这个管道,那么新的管道体现的RequestDelegate自然成为了输出结果。所以中间件自然就表示成输入和输出均为RequestDelegate的Func<RequestDelegate, RequestDelegate>对象。
IapplicationBuilder接口中定义Use方法
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
7、ApplicationBuilder 第四个对象
ApplicationBuilder是我们认识的第四个核心对象。从命名来看,这是我们接触到的第二个Builder,既然它被命名为ApplicationBuilder,意味着由它构建的就是一个Application。那么在ASP.NET Core框架的语义下应用(Application)又具有怎样的表达呢?
对于这个问题,我们可以这样来理解:既然Pipeline = Server + HttpHandler,那么用来处理请求的HttpHandler不就承载了当前应用的所有职责吗?那么HttpHandler就等于Application,由于HttpHandler通过RequestDelegate表示,那么由ApplicationBuilder构建的Application就是一个RequestDelegate对象。

由于表示HttpHandler的RequestDelegate是由注册的中间件来构建的,所以ApplicationBuilder还具有注册中间件的功能。基于ApplicationBuilder具有的这两个基本职责,我们可以将对应的接口定义成如下的形式。Use方法用来注册提供的中间件,Build方法则将注册的中间件构建成一个RequestDelegate对象。
public interface IApplicationBuilder
{
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
RequestDelegate Build();
}
如下所示的是针对该接口的具体实现。我们利用一个列表来保存注册的中间件,所以Use方法只需要将提供的中间件添加到这个列表中即可。当Build方法被调用之后,我们只需按照与注册相反的顺序依次执行表示中间件的Func<RequestDelegate, RequestDelegate>对象就能最终构建出代表HttpHandler的RequestDelegate对象。
public class ApplicationBuilder : IApplicationBuilder
{
private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>();
public RequestDelegate Build()
{
_middlewares.Reverse();
return httpContext =>
{
RequestDelegate next = _ => { _.Response.StatusCode = ; return Task.CompletedTask; };
foreach (var middleware in _middlewares)
{
next = middleware(next);
}
return next(httpContext);
};
} public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_middlewares.Add(middleware);
return this;
}
}
在调用第一个中间件(最后注册)的时候,我们创建了一个RequestDelegate作为输入,后者会将响应状态码设置为404。所以如果ASP.NET Core应用在没有注册任何中间的情况下总是会返回一个404的响应。如果所有的中间件在完成了自身的请求处理任务之后都选择将请求向后分发,同样会返回一个404响应。
Core中的IapplicationBuilder定义
// 摘要:Defines a class that provides the mechanisms to configure an application's requestpipeline.
public interface IApplicationBuilder
{
// Gets or sets the System.IServiceProvider that provides access to the application'sservice container.
IServiceProvider ApplicationServices { get; set; }
//
// 摘要:
// Gets the set of HTTP features the application's server provides.
IFeatureCollection ServerFeatures { get; }
//
// 摘要:
// Gets a key/value collection that can be used to share data between middleware.
IDictionary<string, object> Properties { get; } //
// 摘要:
// Builds the delegate used by this application to process HTTP requests.
// 返回结果:
// The request handling delegate.
RequestDelegate Build(); //
// 摘要:
// Creates a new Microsoft.AspNetCore.Builder.IApplicationBuilder that shares the
//Microsoft.AspNetCore.Builder.IApplicationBuilder.Properties of this Microsoft.AspNetCore.Builder.IApplicationBuilder.
//
// 返回结果:
// The new Microsoft.AspNetCore.Builder.IApplicationBuilder.
IApplicationBuilder New();
//
// 摘要:
// Adds a middleware delegate to the application's request pipeline.
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
}
ASP.NET Core框架深度学习(二) 管道对象的更多相关文章
- ASP.NET Core框架深度学习(一) Hello World
对于学习Core的框架,对我帮助最大的一篇文章是Artech的<200行代码,7个对象——让你了解ASP.NET Core框架的本质>,最近我又重新阅读了一遍该文.本系列文章就是结合我的阅 ...
- ASP.NET Core框架深度学习(四)宿主对象
11.WebHost 第六个对象 到目前为止我们已经知道了由一个服务器和多个中间件构成的管道是如何完整针对请求的监听.接收.处理和最终响应的,接下来来讨论这样的管道是如何被构建出来的.管道是在作为应 ...
- ASP.NET Core框架深度学习(三) Server对象
8.Server 第五个对象 服务器在管道中的职责非常明确,当我们启动应用宿主的WebHost的时候,服务它被自动启动.启动后的服务器会绑定到指定的端口进行请求监听,一旦有请求抵达,服务器会根据该 ...
- ASP.NET Core 框架本质学习
本文作为学习过程中的一个记录. 学习文章地址: https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html 一. ASP.N ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...
- 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移
不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(下)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(完)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- 一个Mini的ASP.NET Core框架的实现
一.ASP.NET Core Mini 在2019年1月的微软技术(苏州)俱乐部成立大会上,蒋金楠老师(大内老A)分享了一个名为“ASP.NET Core框架揭秘”的课程,他用不到200行的代码实现了 ...
随机推荐
- Flink概述| 配置
流处理技术的演变 在开源世界里,Apache Storm项目是流处理的先锋.Storm提供了低延迟的流处理,但是它为实时性付出了一些代价:很难实现高吞吐,并且其正确性没能达到通常所需的水平,换句话说, ...
- ReactNative: 使用像素密度类PixelRatio进行适配
一.简介 现在设备显示屏的清晰度越来越高,尤其是iOS移动设备上的高清适配,Retina显示屏.在开发中,为了保证图片在不同的设备上显示的效果保持一致,往往需要准备多套图片,比如iOS开发中的@1x, ...
- 【ST开发板评测】Nucleo-F411RE开箱报告
前言 面包板又举办开发板试用活动了,很荣幸能获得一块ST官方的Nucleo-F411RE开发板,感谢面包板社区和ST意法半导体的赞助,这是我第一次试用官方的开发板,收到板子后查了一些关于ST官方开发板 ...
- javascript数组在指定位置添加和删除元素
在JavaScript中,Array对象提供了一个强大的splice()方法,利用这个方法可以达到在数组的指定位置添加和删除元素的目的. 指定位置删除元素 要在指定位置删除元素,可以使用splice( ...
- js 轮播图效果
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- URL跳转绕过姿势
POC "@" http://www.target.com/redirecturl=http://whitelist.com@evil.com "\" http ...
- echarts玩转图表之矩形树图
前言 这是第一次用makedown编辑器写文章,感觉像一件利器,排版美观而且效率飙升.进入正题 Echart官网文档地址 针对于矩形树图api配置项链接 1. 完全从数据定义图形 $.get( &qu ...
- 快速掌握MIPI开发攻略,对接百度人工智能计算卡EdgeBoard
MIPI(移动行业处理器接口)是Mobile Industry Processor Interface的缩写,是MIPI联盟发起的为移动应⽤处理器制定的开放标准.MIPI采⽤高速串行接口传输数据,满⾜ ...
- R Data Frame
https://www.datamentor.io/r-programming/data-frame/ Check if a variable is a data frame or not We ca ...
- SQL注入神器———Sqlmap!
Sqlmap 开源,python编写 支持5种SQL注入检测技术: 1.基于布尔的注入检测 2.基于时间的注入检测 3.基于错误的注入检测 4.基于UNION联合查询的检测 5.基于堆叠查询的检测 支 ...