ASP.NET MVC 中是否存在视图?

在呈现视图之前,是否可以确定控制器中是否存在特定的视图名称?

我需要动态确定要呈现的视图的名称。如果存在一个具有该名称的视图,那么我需要呈现该视图。如果没有自定义名称的视图,那么我需要呈现一个默认视图。

我希望在我的控制器中执行类似于以下代码的操作:

public ActionResult Index()
{
var name = SomeMethodToGetViewName();


// The 'ViewExists' method is what I've been unable to find.
if (ViewExists(name))
{
retun View(name);
}
else
{
return View();
}
}
20091 次浏览

What about trying something like the following assuming you are using only one view engine:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
 private bool ViewExists(string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}

For those looking for a copy/paste extension method:

public static class ControllerExtensions
{
public static bool ViewExists(this Controller controller, string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(controller.ControllerContext, name, null);
return (result.View != null);
}
}

If you want to re-use this across multiple controller actions, building on the solution given by Dave, you can define a custom view result as follows:

public class CustomViewResult : ViewResult
{
protected override ViewEngineResult FindView(ControllerContext context)
{
string name = SomeMethodToGetViewName();


ViewEngineResult result = ViewEngines.Engines.FindView(context, name, null);


if (result.View != null)
{
return result;
}


return base.FindView(context);
}


...
}

Then in your action simply return an instance of your custom view:

public ActionResult Index()
{
return new CustomViewResult();
}

Here's another [not necessarily recommended] way of doing it

 try
{
@Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
}
catch (InvalidOperationException) { }
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

My 2 cents.

In asp.net core 2.x and aspnet6 the ViewEngines property no longer exists so we have to use the ICompositeViewEngine service. This a variant of the accepted answer using dependency injection:

public class DemoController : Controller
{
private readonly IViewEngine _viewEngine;


public DemoController(ICompositeViewEngine viewEngine)
{
_viewEngine = viewEngine;
}


private bool ViewExists(string name)
{
ViewEngineResult viewEngineResult = _viewEngine.FindView(ControllerContext, name, true);
return viewEngineResult?.View != null;
}


public ActionResult Index() ...
}

For the curious: The base interface IViewEngine is not registered as a service so we must inject ICompositeViewEngine instead. The FindView() method however is provided by IViewEngine so the member variable may use the base interface.

Here's how to do it in Razor for Core 2.2 etc. Note that the call is "GetView", not "Find View)

@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject ICompositeViewEngine Engine
...
@if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success)
{
@await Html.PartialAsync(scriptName)
}