.NET Core開發日誌——HttpContext

来源:https://www.cnblogs.com/kenwoo/archive/2018/07/26/9369637.html
-Advertisement-
Play Games

之前的文章記述了 "從ASP.NET Core Module到KestrelServer" 的請求處理過程。現在該聊聊如何生成ASP.NET中我們所熟悉的HttpContext。 當KestrelServer啟動時,會綁定相應的IP地址,同時在綁定時將加入HttpConnectionMiddlewa ...


之前的文章記述了從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方法。之後的事情便是HttpHost與HttpApplication的工作了。

那麼費了這麼多工夫,所生成的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里的代碼,畢竟它已是一個較成熟的產品,其中有許多值得借鑒的地方。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • String類的概述 String 類代表字元串。Java 程式中的所有字元串字面值(如 "abc" )都作為此類的實例實現。字元串是常量,一旦被賦值,就不能被改變。 String類的構造方法 * public String():空構造 * public String(byte[] bytes):把 ...
  • 本文來自作者 未聞 在 GitChat 分享的{基於 Docker 的微服務架構實踐} 前言 基於 Docker 的容器技術是在2015年的時候開始接觸的,兩年多的時間,作為一名 Docker 的 DevOps,也見證了 Docker 的技術體系的快速發展。本文主要是結合在公司搭建的微服務架構的實踐 ...
  • ...
  • 由於在C++項目中,經常遇到處理字元方面的問題,故藉此機會整理一下,讓自己對於char , string 等有進一步的瞭解。 基本概念 由單引號括起來的一個字元成為char型字面值。雙引號括起來的零個或多個字元則構成字元串型字面值。 字元串字面值得類型=>由常量字元構成的數組,併在結尾處添加一個空字 ...
  • 進群:125240963 即可獲取數十套PDF哦! 進群:125240963 即可獲取數十套PDF哦! 前面幾天想看一個電影(至於什麼電影就不說了),搜了半天沒有中文字幕。 看日本電影再也不怕看不懂了,6行Python代碼輕鬆實現音頻轉文字 這麼貴! 好在這難道不了一個吃苦耐勞的程式員,在知乎某位大 ...
  • 元組 1.與字元串相同的是元組是一些元素的不可變有序序列。與字元串的區別是元組中的元素不一定是字元,其中的按個元素可以是任意類型,且他們彼此之間的類型也可以不同。 2.元組可以進行的操作: 重覆操作、連接、索引、切片... 3*('a',2) = ('a',2,'a',2,'a',2) ('a',2 ...
  • 題意 世上最良心題目描述qwq 平面上有N個點. 求出所有以這N個點為頂點的三角形的面積和 N<=3000 Sol 直接模擬是$n^3$的。 考慮先枚舉一個$i$,那麼我們要算的就是$\sum_{j = 1}^n sum_{k = j + 1}^n |Cross((a_j, b_j), (a_k, ...
  • 方法一 1.把api-ms-win-crt-runtime-l1-1-0.dll下載到電腦 2.直接拷貝該文件到系統目錄里:C:\Windows\System32目錄下,64位系統為:C:\Windows\SysWOW64 3.最後在開始菜單中找到“運行(R)” 或者按快捷鍵“Win+R”,在彈出的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...