• DotNetCore深入了解之二HttpContext类


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

     1 public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
     2 {
     3     try
     4     {
     5         ...
     6 
     7         async Task OnBind(ListenOptions endpoint)
     8         {
     9             // Add the HTTP middleware as the terminal connection middleware
    10             endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols);
    11 
    12             var connectionDelegate = endpoint.Build();
    13 
    14             // Add the connection limit middleware
    15             if (Options.Limits.MaxConcurrentConnections.HasValue)
    16             {
    17                 connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync;
    18             }
    19 
    20             var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate);
    21             var transport = _transportFactory.Create(endpoint, connectionDispatcher);
    22             _transports.Add(transport);
    23 
    24             await transport.BindAsync().ConfigureAwait(false);
    25         }
    26 
    27         await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false);
    28     }
    29 
    30     ...
    31 }
    View Code
    1 public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols)
    2 {
    3     var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application, protocols);
    4     return builder.Use(next =>
    5     {
    6         return middleware.OnConnectionAsync;
    7     });
    8 }
    View Code

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

     1 public async Task OnConnectionAsync(ConnectionContext connectionContext)
     2 {
     3     ...
     4 
     5     var connection = new HttpConnection(httpConnectionContext);
     6     _serviceContext.ConnectionManager.AddConnection(httpConnectionId, connection);
     7 
     8     try
     9     {
    10         var processingTask = connection.ProcessRequestsAsync(_application);
    11 
    12         ...
    13     }
    14     ...
    15 }
    View Code

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

     1 public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
     2 {
     3     try
     4     {
     5         ...
     6 
     7         lock (_protocolSelectionLock)
     8         {
     9             // Ensure that the connection hasn't already been stopped.
    10             if (_protocolSelectionState == ProtocolSelectionState.Initializing)
    11             {
    12                 switch (SelectProtocol())
    13                 {
    14                     case HttpProtocols.Http1:
    15                         // _http1Connection must be initialized before adding the connection to the connection manager
    16                         requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application);
    17                         _protocolSelectionState = ProtocolSelectionState.Selected;
    18                         break;
    19                     case HttpProtocols.Http2:
    20                         // _http2Connection must be initialized before yielding control to the transport thread,
    21                         // to prevent a race condition where _http2Connection.Abort() is called just as
    22                         // _http2Connection is about to be initialized.
    23                         requestProcessor = CreateHttp2Connection(_adaptedTransport, application);
    24                         _protocolSelectionState = ProtocolSelectionState.Selected;
    25                         break;
    26                     case HttpProtocols.None:
    27                         // An error was already logged in SelectProtocol(), but we should close the connection.
    28                         Abort(ex: null);
    29                         break;
    30                     default:
    31                         // SelectProtocol() only returns Http1, Http2 or None.
    32                         throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None.");
    33                 }
    34 
    35                 _requestProcessor = requestProcessor;
    36             }
    37         }
    38 
    39         if (requestProcessor != null)
    40         {
    41             await requestProcessor.ProcessRequestsAsync(httpApplication);
    42         }
    43 
    44         await adaptedPipelineTask;
    45         await _socketClosedTcs.Task;
    46     }
    47     ...
    48 }
    View Code

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

     1 private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application)
     2 {
     3     // Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value
     4     _keepAlive = true;
     5 
     6     while (_keepAlive)
     7     {
     8         ...
     9 
    10         var httpContext = application.CreateContext(this);
    11 
    12         try
    13         {
    14             KestrelEventSource.Log.RequestStart(this);
    15 
    16             // Run the application code for this request
    17             await application.ProcessRequestAsync(httpContext);
    18 
    19             if (_ioCompleted == 0)
    20             {
    21                 VerifyResponseContentLength();
    22             }
    23         }
    24         ...
    25     }
    26 }
    View Code

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

     1 public Context CreateContext(IFeatureCollection contextFeatures)
     2 {
     3     var context = new Context();
     4     var httpContext = _httpContextFactory.Create(contextFeatures);
     5 
     6     _diagnostics.BeginRequest(httpContext, ref context);
     7 
     8     context.HttpContext = httpContext;
     9     return context;
    10 }
    View Code

    HttpContextFactory类才是最后的一站。

     1 public HttpContext Create(IFeatureCollection featureCollection)
     2 {
     3     if (featureCollection == null)
     4     {
     5         throw new ArgumentNullException(nameof(featureCollection));
     6     }
     7 
     8     var httpContext = new DefaultHttpContext(featureCollection);
     9     if (_httpContextAccessor != null)
    10     {
    11         _httpContextAccessor.HttpContext = httpContext;
    12     }
    13 
    14     var formFeature = new FormFeature(httpContext.Request, _formOptions);
    15     featureCollection.Set<IFormFeature>(formFeature);
    16 
    17     return httpContext;
    18 }
    View Code

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

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

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

  • 相关阅读:
    WP2Sinablog使用后的后遗症
    .NET Framework 4 与 .NET Framework 4 Client Profile
    360浏览器也有了开发人员工具了
    让你的手机号“变”qq号
    首次体验 Live Writter
    判断素数
    SqlServer文件挂起
    vni—2015:验证错误
    成功者善于模仿(转)
    dbms_output缓冲区溢出
  • 原文地址:https://www.cnblogs.com/lizhizhang/p/9383852.html
Copyright © 2020-2023  润新知