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


95

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

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

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

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

14
仅仅阅读它的标题,它似乎是一个非常深刻的哲学问题。

Answers:


154
 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

暂且说一句:我们的一位工程师(由于继续前进)构建了一个自定义视图引擎(称为MultiTenantViewEngine,因此您可以了解其用途),该实现引擎实现FindView在找不到给定给定值的情况下抛出HttpException(404)视图。这是好习惯吗?我不知道。但是,如果有这样的其他实现,也不会感到惊讶。由于在执行此代码时您将不了解视图引擎的内部工作原理,因此您可能希望抛出一个catch {return false; 为了安全起见,请围绕这只小狗。
Brian Colavito 2013年

1
@SOReader,我进行了测试,但IController控制器= new HomeController(); 然后controller.ControllerContext将提供您可以传递给findview方法的东西。
维沙尔·沙尔玛(Vishal Sharma)2013年

感谢您的回答。它帮助我解决了另一个问题。我需要检查我的视图是否为局部视图,并且因为我所有的局部视图名称都以下划线开头,所以我现在可以使用解决方案检查“ result.View!= null”
Deise Vicentin

19

假设您仅使用一个视图引擎,尝试以下类似的操作:

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

看起来这是在接受答案的3分钟前发布的,但还没有爱吗?向我+1。
Trevor de Koekkoek

@TrevordeKoekkoek ... hmmm ... + 1
Vishal Sharma

8

这是另一种[不一定推荐]的方式

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }

这是为了测试.cshtml文件中部分视图的存在。这并不是一个真正的答案,但是链接到这里的另一个问题被错误地关闭,因此我将答案留在这里
Simon_Weaver

2
实际上,这是我可以使用的,因为我正在寻找一种使用特定于文化的局部视图的方法。因此,我只是使用特定于区域性的视图名称来调用它,然后在catch中调用默认视图。而且我是在实用程序函数中执行此操作的,因此我无法访问ControllerContextFindView方法所需的。
敬畏

2

如果要在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

我的2美分。


1

在asp.net core 2.x中,该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未注册为服务,因此必须注入ICompositeViewEngineFindView()但是,方法是由提供的,IViewEngine因此成员变量可以使用基本接口。


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)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.