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

100

在控制器渲染视图之前,能否确定特定视图名称是否存在?

我需要动态确定要渲染的视图名称。如果存在此名称的视图,则需要渲染该视图。如果没有自定义名称的视图,则需要渲染默认视图。

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

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();
    }
}

15
看标题似乎是一个非常深奥的哲学问题。 - user1752532
7个回答

163
 private bool ViewExists(string name)
 {
     ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
     return (result.View != null);
 }

如果你正在寻找一个复制/粘贴的扩展方法:

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);
    }
}

2
这可能更好。我不知道ViewEngines集合本身有一个FindView方法。 - Lance Harper
1
但如何检查其他控制器是否存在该视图? - SOReader
1
有点离题:我们的一位工程师(已经离职)构建了一个自定义视图引擎(称为MultiTenantViewEngine,这样你就能理解它的目的),它实现了FindView方法,如果找不到给定的视图,则抛出HttpException(404)。这是一个好的做法吗?我不知道。但如果有其他类似的实现,也不会感到惊讶。由于在执行此代码时您无法了解视图引擎的内部工作原理,您可能希望在这个地方加上一个catch { return false; },以确保安全。 - Brian Colavito
1
@SOReader,我还没有测试过,但是,IController controller = new HomeController(); 然后 controller.ControllerContext 将会给你一个东西,你可以传递给 findview 方法。 - Vishal Sharma
在 aspnet6 中,ViewEngines 现在在哪里? - zwcloud
显示剩余2条评论

20

如果你只使用了一个视图引擎,可以尝试以下方法:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;

看起来这个问题在被接受的答案发布前3分钟就已经被提出了,但是还没有得到关注?!我给你点赞。 - Trevor de Koekkoek
@TrevordeKoekkoek ...嗯...+1 - Vishal Sharma

8
这里有另一种方法[不一定推荐]可以做到这一点。
 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }

这是用于测试.cshtml文件中部分视图存在性的内容。这并不是对这个问题的真正答案,但是另一个链接到这里的问题被错误地关闭了,所以我把我的答案留在这里。 - Simon_Weaver
2
这对我的需求非常贴切,因为我正在寻找一种使用特定文化的局部视图的方法。所以我只需使用特定文化的视图名称调用它,然后在 catch 中调用默认视图。而且我是在一个实用函数中执行此操作,因此无法访问 ControllerContext,因为 FindView 方法需要它。 - awe

5
在asp.net core 2.x和aspnet6中,ViewEngines属性已不存在,因此我们需要使用ICompositeViewEngine服务。以下是使用依赖注入的变体答案:
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() ...
}

好奇的人:基础接口IViewEngine未注册为服务,因此我们必须注入ICompositeViewEngine。然而,FindView()方法是由IViewEngine提供的,因此成员变量可以使用基础接口。


这在 aspnet6 中也适用。 - zwcloud

3

如果您想在多个控制器操作中重复使用此功能,可以基于Dave提供的解决方案定义自定义视图结果,如下所示:

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);
    }

    ...
}

然后在您的动作中,只需返回自定义视图的实例:

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

1
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

我的想法。

0

以下是如何在Core 2.2等版本中使用Razor进行操作。请注意,调用的方法是“GetView”,而不是“Find View”。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接