ASP.NET Core學習總結(1)

来源:https://www.cnblogs.com/xsddxz/archive/2018/05/19/9039253.html
-Advertisement-
Play Games

經過那麼長時間的學習,終於想給自己這段時間的學習工作做個總結了。記得剛開始學習的時候,什麼資料都沒有,光就啃文檔。不過,值得慶幸的是,自己總算還有一些Web開發的基礎。至少ASP.NET的WebForm和MVC那一套還是有所瞭解的,雖然也不是很精通。說起來,那時候對整個網路應用的整體流程以及什麼HT ...


  經過那麼長時間的學習,終於想給自己這段時間的學習工作做個總結了。記得剛開始學習的時候,什麼資料都沒有,光就啃文檔。不過,值得慶幸的是,自己總算還有一些Web開發的基礎。至少ASP.NET的WebForm和MVC那一套還是有所瞭解的,雖然也不是很精通。說起來,那時候對整個網路應用的整體流程以及什麼HTTP協議都不是很瞭解。終歸是在微軟爸爸的庇護下艱難的成長。

1、概念

  概念這種東西,感覺還是太過於學術化。也就是時間長了,慢慢就能理解的一些經常用到的詞而已。對於大多數人來說,我們幾乎每天都會瀏覽網頁。也許,我們對於網路應用的基本認識,就是從這裡開始的。可惜,很多人的認識仍然停留在打開瀏覽器看網頁上。以至於,對於網頁是怎麼來的,怎麼呈現的毫無概念。

  網路應用是一種分散式系統,通常由客戶端和服務端組成。通過HTTP協議進行通信,是一種請求/應答模式。瀏覽器通常作為客戶端,而我們開發的Web應用,通常作為服務端。

2、原理

 

  上面這張圖來源於微軟的官方文檔,它簡單直觀的描述了我們將要開發的Web應用的基本原理。首先,ASP.NET Core application代表了我們的整個Web應用,它通過HTTP協議與外部進行通信。而在我們程式的內部,首先就是由ASP.NET Core的框架所支配的。Kestrel是一個可以監聽和響應請求的底層服務,它會把接收到的HTTP報文封裝成HttpContext傳遞給我們的應用程式代碼。同時,把應用程式處理好的響應轉換為響應報文返回給客戶端。

​ 現在,讓我們深入應用程式代碼的內部。應用程式管道,本質上是由一個委托鏈構成的。這個名為RequestDelegate的委托有兩個參數,第一個是httpContext,第二個是next,類型也是RequestDeletage,指向下一個委托。

  

  由上圖我們看到,請求和響應實質上是由一系列中間件處理共同處理的。而事實上,這些中間件最終會編譯為一個委托鏈(所有Middleware類按照約定都應該包含一個Invoke方法和構造函數,構造函數中包含了next,Invoke中包含了httpContext)。總結來說,當請求進來以後,首先會執行第一個委托。而第一個委托的內部可以選擇是否調用下一個委托。如同上圖所示,如果第一中間件,實質上會變成一個委托,不調用next()。那麼,請求便在該中間件短路了,即請求不再向下傳遞,而是直接返迴響應了。

  接下來,我們來介紹ASP.NET Core框架的核心部分,即MVC。對於每一個請求來說,應該都會有一個對應的URL。而我們的程式通常也會有一個對應的處理方法,即我們的控制器動作。現在,框架所解決的第一個問題即是,如何根據URL映射到對應的處理方法,即路由機制。

  路由機制是由Microsoft.AspNetCore.Routing實現的。它最核心的部分是RouterMiddleware中的那段代碼。

        public async Task Invoke(HttpContext httpContext)
        {
            var context = new RouteContext(httpContext);
            context.RouteData.Routers.Add(_router);//_router是通過依賴註入,從服務容器中獲得的

              //這一步是最重要的,它會根據RouteContext尋找一個合適的Handler
              //也就是說,整個路由匹配在於這一步是如何實現的
            await _router.RouteAsync(context);

            if (context.Handler == null)
            {
                _logger.RequestDidNotMatchRoutes();
                await _next.Invoke(httpContext);
            }
            else
            {
                httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
                {
                    RouteData = context.RouteData,
                };

                await context.Handler(context.HttpContext);
            }
        }

  上面這個_router是一個IRouter介面類型的變數。實質上,當我們在註冊MVC服務的時候,已經添加了實現類。如下所示:

            //
            // Route Handlers
            //
            services.TryAddSingleton<MvcRouteHandler>(); // Only one per app
            services.TryAddTransient<MvcAttributeRouteHandler>(); // Many per app
            var routes = new RouteBuilder(app)
            {
                DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
            };

            configureRoutes(routes);

            routes.Routes.Insert(0,AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));

            return app.UseRouter(routes.Build());

  我們說,MvcRouteHandler和MvcAttributeRouteHandler都實現了IRouter介面。第一部分的代碼的意圖在於將這兩個Handler添加到服務容器。而第二部分代碼的意圖在於將它們從服務容器中取出,傳遞給路由中間件。

​ 那麼,問題在於,MvcRouteHandler和MvcAttributeRouteHandler是如何實現的?首先,我們來看看MvcRouteHandler內部是如何實現的。

public Task RouteAsync(RouteContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

              //海選
            var candidates = _actionSelector.SelectCandidates(context);
            if (candidates == null || candidates.Count == 0)
            {
                _logger.NoActionsMatched(context.RouteData.Values);
                return Task.CompletedTask;
            }
            
              //精選
            var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates);
            if (actionDescriptor == null)
            {
                _logger.NoActionsMatched(context.RouteData.Values);
                return Task.CompletedTask;
            }

              //使用lambda表達式編譯成RequestDelegate
            context.Handler = (c) =>
            {
                var routeData = c.GetRouteData();

                var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
                if (_actionContextAccessor != null)
                {
                    _actionContextAccessor.ActionContext = actionContext;
                }

                var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
                if (invoker == null)
                {
                    throw new InvalidOperationException(
                        Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                            actionDescriptor.DisplayName));
                }

                  //這裡才是核心處理部分
                return invoker.InvokeAsync();
            };

            return Task.CompletedTask;
        }

  我們來解釋一下,關鍵在於_actionSelector。它會根據routeContext挑選出candidates(候選者),這是第一步。這一步只是篩選出了所有URL匹配的Action,而第二步則需要繼續考慮路由約束的問題。如果第二步還是有多個符合條件的Action,則會引發異常。第三步我們看到,利用一個lambda表達式生成RequestDelegate的委托,即一個Handler(前面我們曾看到在路由中間件中調用)。在這個委托中我們需要關註的是invoker,它是一個IActionInvoker類型的變數。它的預設實現通常是ControllerActionInvoker,我們稍後會深入討論這個類的內部實現。我們看到,invoker是由工廠函數根據actionContext生成的,最終調用了InvokeAsync方法。也就是說,至此為止,我們還是無法得知我們所編寫的Action代碼是怎樣執行的。而為了知道這一點,我們只能繼續深入。

  我們看到,invoker是由_actionInvokerFactory創建的。而_actionInvokerFactory是IActionInvokerFactory類型,從服務容器中獲取。我們來查找它是怎樣註入到容器中的。

            //
            // Action Invoker
            //
            // The IActionInvokerFactory is cachable
            services.TryAddSingleton<IActionInvokerFactory, ActionInvokerFactory>();
            services.TryAddEnumerable(
                ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());

  可以看到,它註入了一個預設實現,ActionInvokerFactory。它的內部是這樣的:

 public IActionInvoker CreateInvoker(ActionContext actionContext)
        {
            var context = new ActionInvokerProviderContext(actionContext);

            foreach (var provider in _actionInvokerProviders)
            {
                provider.OnProvidersExecuting(context);
            }

            for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--)
            {
                _actionInvokerProviders[i].OnProvidersExecuted(context);
            }

            return context.Result;
        }

  事實上,ActionInvokerFactory並沒有直接處理,而是交給了IActionInokerProvider。而在上面我們看到它的預設實現是ControllerActionInvokerProvider。

public void OnProvidersExecuting(ActionInvokerProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor)
            {
                var controllerContext = new ControllerContext(context.ActionContext);
                // PERF: These are rarely going to be changed, so let's go copy-on-write.
                controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);
                controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;

              //緩存策略
                var cacheResult = _controllerActionInvokerCache.GetCachedResult(controllerContext);

                var invoker = new ControllerActionInvoker(
                    _logger,
                    _diagnosticSource,
                    controllerContext,
                    cacheResult.cacheEntry,
                    cacheResult.filters);

                context.Result = invoker;
            }
        }

 我們最終發現,invoker來源於這裡,其中還做了緩存策略。現在,是時候揭開這個ControllerActionInvoker的神秘面紗了。


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

-Advertisement-
Play Games
更多相關文章
  • Java集合類的詳解與應用 集合簡介: 1.定義:可以同時存儲不同類型的數據 他的存儲空間會隨著數據的增大而增大 2.缺點:只能存儲引用數據類型 3.優點:更加合理的利用空間,封裝了更多的方法,用起來更加方便 4.分類:集合分為:Collection(介面): List介面:ArrayList類,L ...
  • 圖論是演算法競賽的一大板塊,二分圖又是其中一個重要的特殊模型——好像有點像網路流QwQ 例題:eXam(SGU 172)、The Perfect Stall(POJ 1274)、Machine Schedule(POJ 1325) ...
  • BitAdminCore是基於NET Core2.0的後端快速開發框架,本篇主要目標是介紹如何使用框架開發應用。框架的一些特性等。 系列鏈接 BitAdminCore框架應用篇:(一)使用Cookiecutter創建應用項目 BitAdminCore框架應用篇:(二)創建一個簡單的增刪改查模塊 ...
  • 64位的access一定要用64的程式才能正確打開,仍然用"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Test.accdb;Persist Security Info=False"即可。 ...
  • 連接字元串是這樣的:connectionString="server=IP地址;database=資料庫名稱;uid=sa;pwd=123456;" 並不是利用mdf文件連接:connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename= |... ...
  • NET Core應用框架之BitAdminCore框架應用篇系列 一、簡介 增刪查改是管理程式最基礎的模式,以下介紹BitAdminCore是如何用最快的效率創建一個模塊。 二、建表 第一件事就是建表,本示例提供最簡單表結構,複雜的功能會在後續一步步釋放。 三、生成實體 1、生成實體之前項目要保證編 ...
  • 自定義配置信息的高級應用 通過上篇博文對簡單的自定義配置信息的學習,使得更加靈活的控制系統配置信息。實際項目中,這種配置的靈活度往往無法滿足項目的靈活度和擴展性。 比如,一個配置信息有三部分組成,而每部分中有包括一些配置信息。僅僅使用簡單的自定義配置無法滿足,因此,需要提供更多的自定義配置方法來靈活 ...
  • 20180519更新內容 昨天更新的版本,早上自己下載下來發現創建項目不成功。 這個問題已經多次出現,主要是cookiecutter編碼問題,項目引用大量外部js文件,部分文件在複製的時候編碼較驗不通過,但我們又不得不用。 所以解決方法沒得選,肯定是打包的時候不要包含這些文件,在項目生成後再還原。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...