[創意料理] 在 ASP.NET MVC 切莫無腦地使用 Html.Action、Html.RenderAction

Html.ActionHtml.RenderActionHtml.PartialHtml.RenderPartial 這四種方法都可以協助我們在 View 裡面渲染部分 HTML 內容,網路上針對這四種方式的差異說明大都著重在使用方式,但這次我們往下挖,看看這四種方式做了些什麼事?

有無 Render 字樣的差別

這四種方法都是 HtmlHelper 的擴充方法,Html.Partial、Html.RenderPartial 分別來自 PartialExtensions.csRenderPartialExtensions.cs,而 Html.Action、Html.RenderAction 而來自於 ChildActionExtensions.cs

有無 Render 字樣的差別看 Source Code 就可以知道了,有 Render 字樣的方法會將 HTML 渲染結果直接寫入 ViewContextWriter,沒有 Render 字樣的方法則將 HTML 渲染結果裝入 MvcHtmlString 裡面回傳,也因此沒有 Render 字樣的方法效能會略遜一咪咪,但我個人覺得這個可以不必糾結,反而下面的這個差別才需要在意。

Partial 與 Action 的差別

Html.Action、Html.RenderAction、Html.Partial、Html.RenderPartial 這四種方法既然都可以協助我們在 View 裡面渲染部分 HTML 內容,往往我們操起鍵盤看哪個順眼就抓來用,但是我們知道它們背後是怎麼產生 HTML 內容的嗎?

Html.Partial、Html.RenderPartial 都呼叫了一個 RenderPartialInternal 的方法,它來自於 HtmlHelper.cs,方法裡面最後就透過 ViewEngineCollection 產生 View 直接渲染 HTML 結果。

Html.Action、Html.RenderAction 則是都呼叫了一個 ActionHelper 方法,它來自於與 Html.Action 及 Html.RenderAction 同一份原始碼檔,在方法最後產生了一個 ChildActionMvcHandlerHttpHandlerUtil.WrapForServerExecute 方法包裝成另一個 IHttpHandler 之後,再丟給 HttpContext.Server.Execute 方法執行以渲染 HTML 結果。

ChildActionMvcHandler 是誰?查看它的原始碼會發現它繼承了 MvcHandler

這表示了一件事情,Html.Action、Html.RenderAction 跑了從 Controller 建立到消滅的一整個生命週期,這包含了 Authentication FiltersAuthorization Filters,以及 Action Filters,而且不論目標 Action 是否定義在同一個 Controller 裡面,都會建立新的 Controller 實例,這生命週期有多長? 請看下圖:

(圖片來源:https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application

知道了這件事之後,我們就要回頭想想,我們想要在 View 中渲染部分 HTML 內容,是否真的有需要透過 Action 來做?是否有需要跑整個 MvcHandler 的生命週期?Html.Partial 或 Html.RenderPartial 是否無法滿足我們的需要?