日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[中篇]:请求响应

發(fā)布時間:2023/12/4 asp.net 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[中篇]:请求响应 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

《200行代碼,7個對象——讓你了解ASP.NET Core框架的本質(zhì)》讓很多讀者對ASP.NET Core管道有了真實的了解。在過去很長一段時間中,有很多人私信給我:能否按照相同的方式分析一下MVC框架的設(shè)計與實現(xiàn)原理,希望這篇文章能夠滿足你們的需求,源代碼可以通過原文下載。

01

IActionResult

我們在前面將定義在Controller類型中的Action方法簡化成只返回Task或者Void的方法,并讓方法自身去完成包括對請求予以相應(yīng)的所有請求處理任務(wù),但真實的MVC框架并非如此。真正的MVC框架中具有一個名為IActionResult的重要結(jié)構(gòu),顧名思義,IActionResult對象一般會作為Action方法的返回值,針對請求的響應(yīng)任務(wù)基本上會由這個對象來實現(xiàn)。

作為Action方法執(zhí)行結(jié)果旨在對請求做最終響應(yīng)的IActionResult接口同樣具有極為簡單的定義。如下main的代碼片段所示,IActionResult對象針對請求的響應(yīng)實現(xiàn)在它唯一的ExecuteResultAsync方法中,針對待執(zhí)行Action的ActionContext上下文是其唯一的輸入?yún)?shù)。

public?interface?IActionResult {Task?ExecuteResultAsync(ActionContext?context); }

針對不同的請求響應(yīng)需求,MVC框架為我們定義了一系列的IActionResult實現(xiàn)類型,應(yīng)用程序同樣也可以根據(jù)需要定義自己的IActionResult類型。作為演示,我們定義了如下這個ContentResult類型,它將指定的字符串作為響應(yīng)主體的內(nèi)容,具體的內(nèi)容類型(媒體內(nèi)容或者MIME類型)則可以靈活指定。

public?class?ContentResult?:?IActionResult {private?readonly?string?_content;private?readonly?string?_contentType;public?ContentResult(string?content,?string?contentType){_content?????=?content;_contentType?????=?contentType;}public?Task?ExecuteResultAsync(ActionContext?context){var?response?=?context.HttpContext.Response;response.ContentType?=?_contentType;return?response.WriteAsync(_content);} }

由于Action方法可能沒有返回值,為了使Action執(zhí)行流程(執(zhí)行Action方法=>將返回值轉(zhuǎn)化成IActionResult對象=>執(zhí)行IActionResult對象)顯得明確而清晰,我們定義了如下這個“什么都沒做”的NullActionResult類型,它利用靜態(tài)只讀屬性Instance返回一個單例的NullActionResult對象。

public?sealed?class?NullActionResult?:?IActionResult {private?NullActionResult()?{?}public?static?NullActionResult?Instance?{?get;?}?=?new?NullActionResult();public?Task?ExecuteResultAsync(ActionContext?context)?=>?Task.CompletedTask; }

02

執(zhí)行IActionResult對象

接下來我們將Action方法返回類型的約束放寬,除了Task和Void,Action方法的返回類型還可以是IActionResult、Task<IActionResult>和ValueTask<IActionResult>?;谶@個新的約定,我們需要對前面定義的ControllerActionInvoker的InvokeAsync方法作如下的修改。如代碼片段所示,在執(zhí)行目標(biāo)Action方法之后,我們調(diào)用ToActionResultAsync方法將返回對象轉(zhuǎn)換成一個Task<IActionResult>對象,最終針對請求的響應(yīng)只需要直接執(zhí)行這個IActionResult對象即可。

public?class?ControllerActionInvoker?:?IActionInvoker {public?ActionContext?ActionContext?{?get;?}public?ControllerActionInvoker(ActionContext?actionContext)?=>?ActionContext?=?actionContext;public?async?Task?InvokeAsync(){var?actionDescriptor?=?(ControllerActionDescriptor)ActionContext.ActionDescriptor;var?controllerType?=?actionDescriptor.ControllerType;var?requestServies?=?ActionContext.HttpContext.RequestServices;var?controllerInstance?=?ActivatorUtilities.CreateInstance(requestServies,?controllerType);if?(controllerInstance?is?Controller?controller){controller.ActionContext?=?ActionContext;}var?actionMethod?=?actionDescriptor.Method;var?result?=?actionMethod.Invoke(controllerInstance,?new?object[0]);var?actionResult?=?await?ToActionResultAsync(result);await?actionResult.ExecuteResultAsync(ActionContext);}private?async?Task<IActionResult>?ToActionResultAsync(object?result){if?(result?==?null){return?NullActionResult.Instance;}if?(result?is?Task<IActionResult>?taskOfActionResult){return?await?taskOfActionResult;}if?(result?is?ValueTask<IActionResult>?valueTaskOfActionResult){return?await?valueTaskOfActionResult;}if?(result?is?IActionResult?actionResult){return?actionResult;}if?(result?is?Task?task){await?task;return?NullActionResult.Instance;}throw?new?InvalidOperationException("Action?method's?return?value?is?invalid.");} }

我們接下來將前面定義的ContentResult引入到演示實例的FoobarController中。如下面的代碼片段所示,我們將Action方法FooAsync和Bar的返回類型分別替換成Task<IActionResult>和IActionResult,具體返回的都是一個ContentResult對象。兩個ContentResult對象都將同一段HTML片段作為響應(yīng)的主體內(nèi)容,但是FooAsync方法將內(nèi)容類型設(shè)置成 “text/html” ,而Bar方法則將其設(shè)置為 “text/plain” 。

public?class?FoobarController?:?Controller {private?static?readonly?string?_html?= @"<html> <head><title>Hello</title> </head> <body><p>Hello?World!</p> </body> </html>";[HttpGet("/{foo}")]public?Task<IActionResult>?FooAsync(){return?Task.FromResult<IActionResult>(new?ContentResult(_html,?"text/html"));}public?IActionResult?Bar()?=>?new?ContentResult(_html,?"text/plain"); }

演示程序啟動之后,如果采用與前面一樣的URL訪問定義在FoobarController的兩個Action方法,我們會在瀏覽器上得到如下圖所示的輸出結(jié)果。由于FooAsync方法將內(nèi)容類型設(shè)置為 “text/html” ,所以瀏覽器會將返回的內(nèi)容作為一個HTML文檔進行解析,但是Bar方法將內(nèi)容類型設(shè)置為 “text/plain” ,所以返回的內(nèi)容會原封不動地輸出到瀏覽器上。

03

IActionResult類型轉(zhuǎn)化

前面的內(nèi)容對Task方法的返回類型做出了一系列的約束,但是我們知道在真正的MVC框架中,定義在Controller中的Action方法可以采用任意的類型。為了解決這個問題,我們可以考慮Action方法返回的數(shù)據(jù)對象轉(zhuǎn)換成一個IActionResult對象。我們將類型轉(zhuǎn)換規(guī)則定義成通過IActionResultTypeMapper接口表示的服務(wù),針對IActionResult的類型轉(zhuǎn)換體現(xiàn)在Convert方法上。值得一提的是,Convert方法表示待轉(zhuǎn)換的對象的value參數(shù)并不一定是Action方法的返回值,而是具體數(shù)據(jù)對象。如果Action方法的返回值是一個Task<TResult>或者ValueTask<TResult>對象,它們的Result屬性返回的參數(shù)這個待轉(zhuǎn)換的數(shù)據(jù)對象。

public?interface?IActionResultTypeMapper {IActionResult?Convert(object?value,?Type?returnType); }

簡單起見,我們定義了如下這個ActionResultTypeMapper類型將作為模擬框架對IActionResultTypeMapper接口的默認(rèn)實現(xiàn)。如代碼片段所示,Convert方法將返回個內(nèi)容類型為“text/plain”的ContentResult對象,原始對象字符串描述(ToString方法的返回值)將作為響應(yīng)主題的內(nèi)容。

public?class?ActionResultTypeMapper?:?IActionResultTypeMapper {public?IActionResult?Convert(object?value,?Type?returnType)=>?new?ContentResult(value.ToString(),?"text/plain"); }

當(dāng)我們將針對Action方法返回類型的限制去除之后,我們的ControllerActionInvoker自然需要作進一步修改。Action方法可能會返回一個Task<TResult>或者ValueTask<TResult>對象(泛型參數(shù)TResult可以是任意類型),所以我們在ControllerActionInvoker類型定義了如下兩個靜態(tài)方法(ConvertFromTaskAsync<TValue>和ConvertFromValueTaskAsync<TValue>)將它們轉(zhuǎn)換成Task<IActionResult>對象,如果返回的不是一個IActionResult對象,作為參數(shù)的IActionResultTypeMapper對象將來進行類型轉(zhuǎn)換。我們定義在兩個靜態(tài)只讀字段(_taskConvertMethod和_valueTaskConvertMethod)來保存描述這兩個泛型方法的MethodInfo對象。

public?class?ControllerActionInvoker?:?IActionInvoker {private?static?readonly?MethodInfo?_taskConvertMethod;private?static?readonly?MethodInfo?_valueTaskConvertMethod;static?ControllerActionInvoker(){var?bindingFlags?=?BindingFlags.Instance?|?BindingFlags.NonPublic|?BindingFlags.Static;_taskConvertMethod?=?typeof(ControllerActionInvoker).GetMethod(nameof(ConvertFromTaskAsync),?bindingFlags);_valueTaskConvertMethod?=?typeof(ControllerActionInvoker).GetMethod(nameof(ConvertFromValueTaskAsync),?bindingFlags);}private?static?async?Task<IActionResult>?ConvertFromTaskAsync<TValue>(Task<TValue>?returnValue,?IActionResultTypeMapper?mapper){var?result?=?await?returnValue;return?result?is?IActionResult?actionResult??actionResult:?mapper.Convert(result,?typeof(TValue));}private?static?async?Task<IActionResult>?ConvertFromValueTaskAsync<TValue>(ValueTask<TValue>?returnValue,?IActionResultTypeMapper?mapper){var?result?=?await?returnValue;return?result?is?IActionResult?actionResult??actionResult:?mapper.Convert(result,?typeof(TValue));}… }

如下所示的是InvokeAsync方法針對Action的執(zhí)行。在執(zhí)行了目標(biāo)Action方法并得到原始的返回值后,我們調(diào)用了ToActionResultAsync方法將返回值轉(zhuǎn)換成Task<IActionResult>,最終通過執(zhí)行IActionResult對象進而完成所有的請求處理任務(wù)。如果返回類型為Task<TResult>或者ValueTask<TResult>,我們會直接采用反射的方式調(diào)用ConvertFromTaskAsync<TValue>或者ConvertFromValueTaskAsync<TValue>方法(更好的方式是采用表達式樹的方式執(zhí)行類型轉(zhuǎn)換方法以獲得更好的性能)。

public?class?ControllerActionInvoker?:?IActionInvoker {????public?async?Task?InvokeAsync(){var?actionDescriptor?=?(ControllerActionDescriptor)ActionContext.ActionDescriptor;var?controllerType?=?actionDescriptor.ControllerType;var?requestServies?=?ActionContext.HttpContext.RequestServices;var?controllerInstance?=?ActivatorUtilities.CreateInstance(requestServies,?controllerType);if?(controllerInstance?is?Controller?controller){controller.ActionContext?=?ActionContext;}var?actionMethod?=?actionDescriptor.Method;var?returnValue?=?actionMethod.Invoke(controllerInstance,?new?object[0]);var?mapper?=?requestServies.GetRequiredService<IActionResultTypeMapper>();var?actionResult?=?await?ToActionResultAsync(returnValue,?actionMethod.ReturnType,?mapper);await?actionResult.ExecuteResultAsync(ActionContext);}private?Task<IActionResult>?ToActionResultAsync(object?returnValue,?Type?returnType,?IActionResultTypeMapper?mapper){//Nullif?(returnValue?==?null?||?returnType?==?typeof(Task)?||?returnType?==?typeof(ValueTask)){return?Task.FromResult<?IActionResult?>?(NullActionResult.Instance);}//IActionResultif?(returnValue?is?IActionResult?actionResult){return?Task.FromResult(actionResult);}//Task<TResult>if?(returnType.IsGenericType?&&?returnType.GetGenericTypeDefinition()?==?typeof(Task<>)){var?declaredType?=?returnType.GenericTypeArguments.Single();var?taskOfResult?=?_taskConvertMethod.MakeGenericMethod(declaredType).Invoke(null,?new?object[]?{?returnValue,?mapper?});return?(Task<IActionResult>)taskOfResult;}//ValueTask<TResult>if?(returnType.IsGenericType?&&?returnType.GetGenericTypeDefinition()?==?typeof(ValueTask<>)){var?declaredType?=?returnType.GenericTypeArguments.Single();var?valueTaskOfResult?=?_valueTaskConvertMethod.MakeGenericMethod(declaredType).Invoke(null,?new?object[]?{?returnValue,?mapper?});return?(Task<IActionResult>)valueTaskOfResult;}return?Task.FromResult(mapper.Convert(returnValue,?returnType));} }

從上面的代碼片段可以看出,在進行針對IActionResult的類型轉(zhuǎn)換過程中使用到的IActionResultTypeMapper對象是從針對當(dāng)前請求的依賴注入容器中提取的,所以我們在應(yīng)用啟動之前需要作針對性的服務(wù)注冊。我們將針對IActionResultTypeMapper的服務(wù)注冊添加到之前定義的AddMvcControllers擴展方法中。

public?static?class?ServiceCollectionExtensions {public?static?IServiceCollection?AddMvcControllers(this?IServiceCollection?services){return?services.AddSingleton<IActionDescriptorCollectionProvider,?DefaultActionDescriptorCollectionProvider>().AddSingleton<IActionInvokerFactory,?ActionInvokerFactory>().AddSingleton<IActionDescriptorProvider,?ControllerActionDescriptorProvider>().AddSingleton<ControllerActionEndpointDataSource,?ControllerActionEndpointDataSource>().AddSingleton<IActionResultTypeMapper,?ActionResultTypeMapper>();} }

為了驗證模擬框架對Action方法的任意返回類型的支持,我們將前面演示實例定義的FoobarController做了如下的修改。如代碼片段所示,我們在FoobarController類型中定義了四個Action方法,它們返回的類型分別為Task<ContentResult>、ValueTask<ContentResult>、Task<String>、ValueTask<String>,ContentResult對象的內(nèi)容和直接返回的字符串都是一段相同的HTML。

public?class?FoobarController?:?Controller {private?static?readonly?string?_html?= @"<html> <head><title>Hello</title> </head> <body><p>Hello?World!</p> </body> </html>";[HttpGet("/foo")]public?Task<ContentResult>?FooAsync()=>?Task.FromResult(new?ContentResult(_html,?"text/html"));[HttpGet("/bar")]public?ValueTask<ContentResult>?BarAsync()=>?new?ValueTask<ContentResult>(new?ContentResult(_html,?"text/html"));[HttpGet("/baz")]public?Task<string>?BazAsync()?=>?Task.FromResult(_html);[HttpGet("/qux")]public?ValueTask<string>?QuxAsync()?=>?new?ValueTask<string>(_html); }

我們在上述四個Action方法上通過標(biāo)注HttpGetAttribute特性將路由模板分別設(shè)置為“/foo”、“/bar”、“/baz”和“/qux”,所以我們可以采用相應(yīng)的URL來訪問這四個Action方法。下圖所示的是這個Action的響應(yīng)內(nèi)容在瀏覽器上的呈現(xiàn)。由于Action方法Baz和Qux返回的是一個字符串,按照ActionResultTypeMapper類型提供的轉(zhuǎn)換規(guī)則,最終返回的將是以此字符串作為響應(yīng)內(nèi)容,內(nèi)容類型為 “text/plain” 的ContentResult對象。

總結(jié)

以上是生活随笔為你收集整理的通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[中篇]:请求响应的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。