Une vue existe-t-elle dans ASP.NET MVC?

95

Est-il possible de déterminer si un nom de vue spécifique existe à partir d'un contrôleur avant de rendre la vue?

J'ai besoin de déterminer dynamiquement le nom de la vue à rendre. Si une vue existe avec ce nom, je dois rendre cette vue. S'il n'y a pas de vue par le nom personnalisé, je dois rendre une vue par défaut.

Je voudrais faire quelque chose de similaire au code suivant dans mon contrôleur:

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();
    }
}
Andrew Hanson
la source
14
Juste en lisant le titre de ceci, cela semble être une question philosophique très profonde.

Réponses:

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

Pour ceux qui recherchent une méthode d'extension copier / coller:

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);
    }
}
Dave Cluderay
la source
2
C'est probablement mieux. Je ne savais pas qu'il y avait une méthode FindView hors de la collection ViewEngines elle-même.
Lance Harper
1
Mais comment vérifier si la vue existe pour un autre contrôleur?
SOReader
Une sorte de aparté: l'un de nos ingénieurs (depuis déménagé) a construit un moteur de vue personnalisé (appelé MultiTenantViewEngine, vous avez donc une idée de son objectif) qui implémente FindView pour lancer une HttpException (404) s'il ne trouve pas le vue. Est-ce une bonne pratique? Je n'ai aucune idée. Mais je ne serais pas surpris s'il existe d'autres implémentations comme ça. Puisque vous ne connaîtrez pas le fonctionnement interne du moteur de vue pendant l'exécution de ce code, vous voudrez peut-être lancer un catch {return false; } autour de ce chiot, juste pour être en sécurité.
Brian Colavito
1
@SOReader, j'ai testé hvnt mais, IController controller = new HomeController (); puis controller.ControllerContext donnera la chose que vous pouvez passer aux méthodes findview.
Vishal Sharma
Merci pour cette réponse. Cela m'a aidé dans un autre problème. J'avais besoin de vérifier si ma vue est partielle ou non et comme le nom de tous mes partiels commence par souligné, je peux maintenant travailler avec ma solution en vérifiant si "result.View! = Null"
Deise Vicentin
19

Pourquoi ne pas essayer quelque chose comme ce qui suit en supposant que vous n'utilisez qu'un seul moteur de vue:

bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
Lance Harper
la source
on dirait que celui-ci a été posté 3 minutes avant la réponse acceptée et pourtant pas d'amour?! +1 de moi.
Trevor de Koekkoek
@TrevordeKoekkoek ... hmmm ... + 1
Vishal Sharma
8

Voici une autre façon [pas nécessairement recommandée] de le faire

 try
 {
     @Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
 }
 catch (InvalidOperationException) { }
Simon_Weaver
la source
c'est pour tester l'existence d'une vue partielle dans un fichier .cshtml. ce n'est pas vraiment une réponse à cette question, mais une autre question qui lie ici a été incorrectement fermée, donc je laisse ma réponse ici
Simon_Weaver
2
C'était en fait parfait pour mon utilisation, car je cherchais un moyen d'utiliser une vue partielle spécifique à la culture. Donc, je viens de l'appeler avec le nom de la vue spécifique à la culture, puis j'ai appelé la vue par défaut à l'intérieur de la capture. Et je faisais cela dans une fonction utilitaire, donc je n'avais pas accès aux besoins de ControllerContextla FindViewméthode.
awe
2

Si vous souhaitez réutiliser ceci sur plusieurs actions de contrôleur, en vous basant sur la solution fournie par Dave, vous pouvez définir un résultat de vue personnalisé comme suit:

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

    ...
}

Ensuite, dans votre action, renvoyez simplement une instance de votre vue personnalisée:

public ActionResult Index()
{ 
    return new CustomViewResult();
}
DSO
la source
1
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null

Mes 2 cents.

tynar
la source
1

Dans asp.net core 2.x, la ViewEnginespropriété n'existe plus, nous devons donc utiliser le ICompositeViewEngineservice. C'est une variante de la réponse acceptée utilisant l'injection de dépendances:

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

Pour les curieux: l'interface de base IViewEnginen'est pas enregistrée en tant que service donc il faut injecter à la ICompositeViewEngineplace. Le FindView()procédé est cependant assurée par IViewEnginesi la variable membre peut utiliser l'interface de base.

Idilov
la source
0

Voici comment procéder dans Razor for Core 2.2, etc. Notez que l'appel est "GetView" et non "Find View"

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