[英]ASP.NET Core MVC - Return ViewResult or JSON from controller action
[英]Where is the ViewResult returned by ASP.NET Core Controller consumed?
在 ASP.NET Core Controller
類的定義中,它將View()
方法定義為返回一個ViewResult
對象。
namespace Microsoft.AspNetCore.Mvc
{
//...
public abstract class Controller : ControllerBase, IActionFilter, IFilterMetadata, IAsyncActionFilter, IDisposable
{
//...
public virtual ViewResult View();
//...
}
}
它在框架中的哪個位置調用了如下所示的控制器方法並使用了調用View()
方法返回的ViewResult
?
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
整個過程相當ViewExecutor
,由ResourceInvoker
類內部管理,但負責處理ViewResult
的主要管道是ViewExecutor
類( 源代碼)。
ViewExecutor
的主要入口點是ExecuteAsync()
方法,它接受ActionContext
和您的ViewResult
。 然后它使用注冊的IViewEngine
(通常是RazorViewEngine
)來定位相應的視圖,以識別相應的視圖並返回一個ViewEngineResult
實例。 然后使用該ViewEngineResult
執行和呈現視圖。
ViewExecutor
如果需要自定義邏輯,可以通過實現IActionResultExecutor<>
接口,然后使用 ASP.NET Core 的依賴注入容器將其注冊為單例來實現自己的視圖執行器類。 這樣做時,我建議參考開箱即用的ViewResultExecutor
的源代碼。
如果您想實現用於定位視圖的自定義邏輯,這會很有用。 例如,您可能想要考慮狀態或上下文數據,例如路由數據、請求標頭、cookie、會話等。 定位視圖時。
因此,作為一個非常簡單的示例,假設您希望允許使用查詢字符串值(例如?View=MyView
)有選擇地選擇?View=MyView
。 在這種情況下,您可以創建一個QueryStringViewResultExecutor
。 這是一個基本的概念驗證:
public class QueryStringViewResultExecutor : ViewExecutor, IActionResultExecutor<ViewResult>
{
public QueryStringViewResultExecutor(
IOptions<MvcViewOptions> viewOptions,
IHttpResponseStreamWriterFactory writerFactory,
ICompositeViewEngine viewEngine,
ITempDataDictionaryFactory tempDataFactory,
DiagnosticListener diagnosticListener,
IModelMetadataProvider modelMetadataProvider
) : base(
viewOptions, writerFactory, viewEngine, tempDataFactory, diagnosticListener, modelMetadataProvider
)
{
}
public async Task ExecuteAsync(ActionContext context, ViewResult result)
{
var viewEngineResult = FindView(context, result); // See helper method below
viewEngineResult.EnsureSuccessful(originalLocations: null);
var view = viewEngineResult.View;
using (view as IDisposable)
{
await ExecuteAsync(
context,
view,
result.ViewData,
result.TempData,
result.ViewName,
result.StatusCode
).ConfigureAwait(false);
}
}
private ViewEngineResult FindView(ActionContext actionContext, ViewResult viewResult)
{
// Define variables
var view = (ViewEngineResult?)null;
var viewEngine = viewResult.ViewEngine?? ViewEngine;
var searchedPaths = new List<string>();
var requestContext = actionContext.HttpContext.Request;
// If defined, attempt to locate view based on query string variable
if (requestContext.Query.ContainsKey("View"))
{
var queryStringValue = requestContext.Query["View"].First<string>();
if (queryStringValue is not null)
{
view = viewEngine.FindView(actionContext, queryStringValue, isMainPage: true);
searchedPaths = searchedPaths.Union(view.SearchedLocations?? Array.Empty<string>()).ToList();
}
}
// If no view is found, fall back to the view defined on the viewResult
if (!view?.Success?? true)
{
view = viewEngine.FindView(actionContext, viewResult.ViewName, isMainPage: true);
searchedPaths = searchedPaths.Union(view.SearchedLocations ?? Array.Empty<string>()).ToList();
}
// Return view for processing by the razor engine
if (view is not null and { Success: true }) {
return view;
}
return ViewEngineResult.NotFound(viewResult.ViewName, searchedPaths);
}
}
然后,您可以在Startup.ConfigureServices()
方法中將其注冊為:
services.Services.TryAddSingleton<IActionResultExecutor<ViewResult>, QueryStringViewResultExecutor>();
免責聲明:您可能需要注銷開箱即用的
ViewExecutor
以避免沖突。 但是,通常情況下,您使用自定義ViewResult
注冊IActionResultExecutor<>
,因此,這不是必需的; 見下文。
ViewResult
通常,您希望將其與自定義ViewResult
配對。 為什么這很有用? 通常是因為您需要從Controller
向ViewResultExecutor
傳遞額外的數據。
因此,作為一個人為的示例,假設您有主題,並且可以根據該主題選擇性地自定義視圖。 然后,您可以將Theme
屬性添加到您的ViewResult
,從而允許ViewResultExecutor
首先查找基於主題的視圖,否則回退到非主題版本。
public class ThemedViewResult : ViewResult
{
public string Theme { get; set; }
public override async Task ExecuteResultAsync(ActionContext context)
{
var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<ThemedViewResult>>();
await executor.ExecuteAsync(context, this).ConfigureAwait(false);
}
}
即使您不需要自定義ViewResult
,也值得暫停一下以評估此代碼。 底層ViewResult
類實現IActionResult
,它公開了一個方法ExecuteResultAsync()
。 這是由頂部提到的ResourceInvoker
類調用的。 這依次定位已注冊的IActionResultExecutor<>
即我們在上一節中創建和注冊的組件類型——並調用其ExecuteAsync()
方法。
注意:這是一個人為的示例,因為通常其他上下文數據可以訪問主題,例如
RouteData
、自定義ClaimsPrincipal
或ISession
實現。 但是您可以想象在其他時候,這些信息將是特定於請求的,並且最好通過ViewResult
從Controller
中繼。 例如,也許是一個 CMS,可以在每頁的基礎上選擇主題。
由於不清楚您是否真的需要擴展此功能,或者只是好奇所有內容如何組合在一起,因此上述示例僅作為概念驗證; 他們沒有經過測試。 盡管如此,它們還是應該讓您基本了解ViewResult
的處理位置和方式,以及在您需要時擴展該行為的選項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.