之前的文章记述了从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里的代码,毕竟它已是一个较成熟的产品,其中有许多值得借鉴的地方。

最新文章

  1. appserv 安装php的memcache扩展。
  2. Oracle基本数据类型
  3. hdu1492(约数个数定理)
  4. 【转载】开发备必:WEB前端开发规范文档
  5. PO/VO/BO等对象模型
  6. java web filter 之一 基础实现
  7. tornado中使用Mako模版
  8. Xfire soapHeader的WebService权限控制forjava
  9. C#中文本模板(.tt)
  10. mybatis-generator 代码自动生成工具(maven方式)
  11. Python基础__函数
  12. 解决WebView加载本地文件乱码
  13. 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下)
  14. tensorflow(3)可视化,日志,调试
  15. Toggle组件切换开关,控制开关图片显示与隐藏
  16. iptables snat 和dnat说明
  17. Jenkins+Docker自动化集成环境搭
  18. Tuple Class
  19. python中的目录递归的创建和目录及其下的文件递归的删除
  20. 小结:ES7——async和await初识

热门文章

  1. tar 的–exclude参数,实现不包括某些文件(转)
  2. Window下对nodejs多版本管理GNVM
  3. Java list对象列表排序 实例
  4. redis竞汰数据同步问题解决
  5. 教你一招:[转载]使用 Easy Sysprep v4 封装 Windows 7 精品
  6. 浏览器关闭、刷新、关闭标签事件,兼容IE8,chrome,firefox
  7. 【HTTP】 认证和单点登录 【瞎写的…】
  8. 【转】WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式
  9. centos下安装Loadrunner
  10. 响应式编程笔记三:一个简单的HTTP服务器