Existe um modo de exibição no ASP.NET MVC?


95

É possível determinar se um nome de visão específico existe de dentro de um controlador antes de renderizar a visão?

Tenho um requisito para determinar dinamicamente o nome da vista a ser renderizada. Se houver uma visualização com esse nome, preciso renderizá-la. Se não houver visualização com o nome personalizado, preciso renderizar uma visualização padrão.

Eu gostaria de fazer algo semelhante ao seguinte código no meu controlador:

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
Só de ler o título disso, parece uma questão filosófica muito profunda.

Respostas:


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

Para quem procura um método de extensão copiar / colar:

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
Provavelmente é melhor. Eu não sabia que havia um método FindView fora da coleção ViewEngines em si.
Lance Harper

1
Mas como verificar se a view existe para outro controlador?
SOReader de

Um pouco à parte: um de nossos engenheiros (desde então) construiu um mecanismo de visualização personalizado (chamado MultiTenantViewEngine, para que você tenha uma noção de seu propósito) que implementa FindView para lançar uma HttpException (404) se não conseguir encontrar o dado Visão. Esta é uma boa prática? Eu não faço ideia. Mas não ficaria surpreso se houver outras implementações como essa. Como você não conhecerá o funcionamento interno do mecanismo de visualização à medida que este código é executado, você pode desejar lançar um catch {return false; } em torno deste cachorrinho, só para garantir.
Brian Colavito

1
@SOReader, eu hvnt testado, mas, controlador IController = new HomeController (); e então controller.ControllerContext fornecerá o que você pode passar para os métodos findview.
Vishal Sharma

Obrigado por esta resposta. Isso me ajudou em outro problema. Eu precisava verificar se minha visão é parcial ou não e como o nome de todos os parciais começa com sublinhado agora posso trabalhar com minha solução verificando se "result.View! = Null"
Deise Vicentin

19

Que tal tentar algo como o seguinte, supondo que você esteja usando apenas um mecanismo de visualização:

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

parece que esse aqui foi postado 3 minutos antes da resposta aceita e mesmo assim sem amor? +1 de mim.
Trevor de Koekkoek

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

8

Aqui está outra maneira [não necessariamente recomendada] de fazer isso

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

isso é para testar a existência de uma visualização parcial em um arquivo .cshtml. não é realmente uma resposta para esta pergunta, mas outra pergunta que links aqui foi fechada incorretamente, então estou deixando minha resposta aqui
Simon_Weaver

2
Isso foi realmente adequado para o meu uso, já que eu estava procurando uma maneira de usar uma visão parcial específica da cultura. Então, chamei isso apenas com o nome de exibição específico da cultura e, em seguida, chamei a exibição padrão dentro da captura. E eu estava fazendo isso em uma função de utilidade, então não tive acesso ao ControllerContextconforme o FindViewmétodo precisa.
admirável

2

Se quiser reutilizar isso em várias ações do controlador, com base na solução fornecida por Dave, você pode definir um resultado de visualização personalizado da seguinte maneira:

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

    ...
}

Então, em sua ação, basta retornar uma instância de sua visualização personalizada:

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

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

Meus 2 centavos.


1

No asp.net core 2.x o ViewEngines propriedade não existe mais, então temos que usar o ICompositeViewEngineserviço. Esta é uma variante da resposta aceita usando injeção de dependência:

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

Para os curiosos: A interface base IViewEnginenão está registrada como um serviço, então devemos injetar em seu ICompositeViewEnginelugar. O FindView()método, entretanto, é fornecido por, IViewEngineportanto, a variável de membro pode usar a interface base.


0

Veja como fazer isso no Razor for Core 2.2 etc. Observe que a chamada é "GetView", não "Find View)

@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject ICompositeViewEngine Engine
...
@if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success) 
{
    @await Html.PartialAsync(scriptName)
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.