之前的文章记述了从ASP.NET Core Module到KestrelServer的请求处理过程。现在该聊聊如何生成ASP.NET中我们所熟悉的HttpContext。

当KestrelServer启动时,会绑定相应的IP地址,同时在绑定时将加入HttpConnectionMiddleware作为终端连接的中间件。

public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
try
{
... async Task OnBind(ListenOptions endpoint)
{
// Add the HTTP middleware as the terminal connection middleware
endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols); var connectionDelegate = endpoint.Build(); // Add the connection limit middleware
if (Options.Limits.MaxConcurrentConnections.HasValue)
{
connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync;
} var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate);
var transport = _transportFactory.Create(endpoint, connectionDispatcher);
_transports.Add(transport); await transport.BindAsync().ConfigureAwait(false);
} await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false);
} ...
}
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols)
{
var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application, protocols);
return builder.Use(next =>
{
return middleware.OnConnectionAsync;
});
}

当请求抵达此中间件时,在其OnConnectionAsync方法里会创建HttpConnection对象,并通过该对象处理请求。

public async Task OnConnectionAsync(ConnectionContext connectionContext)
{
... var connection = new HttpConnection(httpConnectionContext);
_serviceContext.ConnectionManager.AddConnection(httpConnectionId, connection); try
{
var processingTask = connection.ProcessRequestsAsync(_application); ...
}
...
}

ProcessRequestsAsync方法内部会根据HTTP协议的不同创建Http1Connection或者Http2Connection对象,一般为Http1Connection。

public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
{
try
{
... lock (_protocolSelectionLock)
{
// Ensure that the connection hasn't already been stopped.
if (_protocolSelectionState == ProtocolSelectionState.Initializing)
{
switch (SelectProtocol())
{
case HttpProtocols.Http1:
// _http1Connection must be initialized before adding the connection to the connection manager
requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application);
_protocolSelectionState = ProtocolSelectionState.Selected;
break;
case HttpProtocols.Http2:
// _http2Connection must be initialized before yielding control to the transport thread,
// to prevent a race condition where _http2Connection.Abort() is called just as
// _http2Connection is about to be initialized.
requestProcessor = CreateHttp2Connection(_adaptedTransport, application);
_protocolSelectionState = ProtocolSelectionState.Selected;
break;
case HttpProtocols.None:
// An error was already logged in SelectProtocol(), but we should close the connection.
Abort(ex: null);
break;
default:
// SelectProtocol() only returns Http1, Http2 or None.
throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None.");
} _requestProcessor = requestProcessor;
}
} if (requestProcessor != null)
{
await requestProcessor.ProcessRequestsAsync(httpApplication);
} await adaptedPipelineTask;
await _socketClosedTcs.Task;
}
...
}

Http1Connection父类HttpProtocol里的ProcessRequests方法会创建一个Context对象,但这还不是最终要找到的HttpContext。

private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application)
{
// Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value
_keepAlive = true; while (_keepAlive)
{
... var httpContext = application.CreateContext(this); try
{
KestrelEventSource.Log.RequestStart(this); // Run the application code for this request
await application.ProcessRequestAsync(httpContext); if (_ioCompleted == 0)
{
VerifyResponseContentLength();
}
}
...
}
}

在HostingApplication类中会看到HttpContext原来是由HttpContextFactory工厂类生成的。

public Context CreateContext(IFeatureCollection contextFeatures)
{
var context = new Context();
var httpContext = _httpContextFactory.Create(contextFeatures); _diagnostics.BeginRequest(httpContext, ref context); context.HttpContext = httpContext;
return context;
}

HttpContextFactory类才是最后的一站。

public HttpContext Create(IFeatureCollection featureCollection)
{
if (featureCollection == null)
{
throw new ArgumentNullException(nameof(featureCollection));
} var httpContext = new DefaultHttpContext(featureCollection);
if (_httpContextAccessor != null)
{
_httpContextAccessor.HttpContext = httpContext;
} var formFeature = new FormFeature(httpContext.Request, _formOptions);
featureCollection.Set<IFormFeature>(formFeature); return httpContext;
}

简单理了张流程图总结一下:

生成的HttpContext对象最终传递到IHttpApplication的ProcessRequestAsync方法。之后的事情便是WebHost与HostingApplication的工作了。

那么费了这么多工夫,所生成的HttpContext究竟有什么用处呢?

先查看MSDN上对它的定义:

Encapsulates all HTTP-specific information about an individual HTTP request.

可以理解为对于每个单独的HTTP请求,其间所创建的HttpContext对象封装了全部所需的HTTP信息。

再看其包含的属性:

public abstract class HttpContext
{
public abstract IFeatureCollection Features { get; }
public abstract HttpRequest Request { get; }
public abstract HttpResponse Response { get; }
public abstract ConnectionInfo Connection { get; }
public abstract WebSocketManager WebSockets { get; }
public abstract AuthenticationManager Authentication { get; }
public abstract ClaimsPrincipal User { get; set; }
public abstract IDictionary<object, object> Items { get; set; }
public abstract IServiceProvider RequestServices { get; set; }
public abstract CancellationToken RequestAborted { get; set; }
public abstract string TraceIdentifier { get; set; }
public abstract ISession Session { get; set; }
public abstract void Abort();
}

请求(Request),响应(Response),会话(Session)这些与HTTP接触时最常见到的名词,都出现在HttpContext对象中。说明在处理HTTP请求时,若是需要获取这些相关信息,完全可以通过调用其属性而得到。

通过传递一个上下文环境参数,以协助获取各环节处理过程中所需的信息,在各种框架中是十分常见的作法。ASP.NET Core里的用法并无特别的创新,但其实用性还是毋庸置疑的。如果想要构建自己的框架时,不妨多参考下ASP.NET Core里的代码,毕竟它已是一个较成熟的产品,其中有许多值得借鉴的地方。

.NET Core开发日志——HttpContext的更多相关文章

  1. .NET Core开发日志——RequestDelegate

    本文主要是对.NET Core开发日志--Middleware的补遗,但是会从看起来平平无奇的RequestDelegate开始叙述,所以以其作为标题,也是合情合理. RequestDelegate是 ...

  2. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  3. .NET Core开发日志——Entity Framework与PostgreSQL

    Entity Framework在.NET Core中被命名为Entity Framework Core.虽然一般会用于对SQL Server数据库进行数据操作,但其实它还支持其它数据库,这里就以Po ...

  4. .NET Core开发日志——从搭建开发环境开始

    .NET Core自2016年推出1.0版本开始,到目前已是2.1版本,在其roadmap计划里明年更会推出3.0版本,发展不可不谓之迅捷.不少公司在经过一个谨慎的观望期后,也逐步开始将系统升级至最新 ...

  5. .NET Core开发日志——Model Binding

    ASP.NET Core MVC中所提供的Model Binding功能简单但实用,其主要目的是将请求中包含的数据映射到action的方法参数中.这样就避免了开发者像在Web Forms时代那样需要从 ...

  6. .NET Core开发日志——简述路由

    有过ASP.NET或其它现代Web框架开发经历的开发者对路由这一名字应该不陌生.如果要用一句话解释什么是路由,可以这样形容:通过对URL的解析,指定相应的处理程序. 回忆下在Web Forms应用程序 ...

  7. .NET Core开发日志——结构化日志

    在.NET生态圈中,最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net.而其后来者,莫过于NLog.Nlog与log4net相比,有一项较显著的优势,它支持结构化日志. 结 ...

  8. .NET Core开发日志——Edge.js

    最近在项目中遇到这样的需求:要将旧有系统的一部分业务逻辑集成到新的自动化流程工具中.这套正在开发的自动化工具使用的是C#语言,而旧有系统的业务逻辑则是使用AngularJS在前端构建而成.所以最初的考 ...

  9. .NET Core开发日志——Linux版本的SQL Server

    SQL Server 2017版本已经可以在Linux系统上安装,但我在尝试.NET Core跨平台开发的时候使用的是Mac系统,所以这里记录了在Mac上安装SQL Server的过程. 最新的SQL ...

随机推荐

  1. 12C -- ORA-12850: 无法在所有指定实例上分配从属进程: 需要 2, 已分配 1

    使用客户端连接到oracle 12.2.0.1 rac数据库,报以下错误信息: ORA-12850: 无法在所有指定实例上分配从属进程: 需要 2, 已分配 1 因为没有mos账号,只好谷歌一下了.找 ...

  2. Atitit 快速开发体系建设路线图

    Atitit 快速开发体系建设路线图 1.1. 项目类型划分 哑铃型 橄榄型  直板型(可以立即实行)1 1.2. 解决方案知识库 最佳实践库 最佳流程优化(已成,需要一些整理)2 1.3. 功能模板 ...

  3. 企业安全建设之搭建开源SIEM平台

    https://www.freebuf.com/special/127172.html https://www.freebuf.com/special/127264.html https://www. ...

  4. 【工具】我的Git学习日志

    使用github一段时间,一直使用的是可视化工具,配合公司转用git,提前联系下git的命令. 安装 windows上安装git 从git for windows下载安装包,我下的是Git-2.13. ...

  5. Android Launcher分析和修改7——AllApp全部应用列表(AppsCustomizeTabHost)

    今天主要是分析一下Launcher里面的所有应用列表.Android4.0 Launcher的所有应用列表跟2.X比较大的区别就是多了Widget的显示.下面会详细分析Launcher里面有关所有应用 ...

  6. 【转】JCR期刊分区及其检索方法

    不少机构依据JCR期刊分区制定科研激励政策,相关科研工作者及科研管理机构密切关注JCR期刊分区及其检索方法.本文作一粗略介绍.    关于JCR(Journal Citation Reports,期刊 ...

  7. Windows server 2008 R2 多用户远程桌面

    1.  创建三个本地管理员测试用户 user01  user02  user03并设置密码 2.  开启远程桌面连接 3.新建规则放行远程桌面端口3389 (或者关闭防火墙) 4.安装远程桌面服务 5 ...

  8. 关于tomcat的session问题

    因为有需要每一个项目有独立端口,并且能够单独启动和关闭,所以在一台服务器上配置了多个tomcat.tomcat是完全一样的,只是各自的端口不一致. 现在的问题是单独启动一个tomcat完全没有问题. ...

  9. mysql 第二高薪水

    编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) +----+--------+ | Id | Salary | +----+--------+ | 1 | 100 | ...

  10. 移除input在type="number"时的上下箭头

    网页在有些情况下,会需要input的输入的为单纯数字的文本框,此时type=number,但使用type=number时,输入框后面会有一个上下箭头,那么如何去掉上下箭头呢? <input ty ...