No MVC3 Razor, como obtenho o html de uma visualização renderizada dentro de uma ação?

90

Alguém sabe como obter o html gerado de uma visão dentro de uma ação?

É algo assim:

public ActionResult Do()
{
    var html = RenderView("hello", model);
...
}
Omu
fonte

Respostas:

152

Eu uso um método estático em uma classe que chamei. Utilities.CommonEu passo as visualizações de volta para o cliente como propriedades de objetos JSON constantemente, então eu tive a necessidade de renderizá-los em uma string. Aqui está:

public static string RenderPartialViewToString(Controller controller, string viewName, object model)
{
    controller.ViewData.Model = model;
    using (StringWriter sw = new StringWriter())
    {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
        viewResult.View.Render(viewContext, sw);

        return sw.ToString();
    }
}

Isso funcionará tanto para visualizações completas quanto parciais, basta alterar ViewEngines.Engines.FindPartialViewpara ViewEngines.Engines.FindView.

Chev
fonte
14
FindView precisa de outro parâmetro ( masterName) que você especificaria nulo. Também recomendo salvar e restaurar (após a renderização) controller.ViewData.Model caso o método seja chamado na instância do controlador atual e o modelo tenha sido atribuído antes desta chamada.
Andrei Rînea
4
Você também deve chamar viewResult.ViewEngine.ReleaseView (controller.ControllerContext, viewResult.View)
sjmeverett
3
Funciona muito bem, mas eu não gostaria de capturar e renderizar exceções em meu código ativo.
pauloya
3
Se você o estiver chamando de dentro do próprio controlador, apenas passe thiscomo o argumento do controlador. RenderPartialViewToString(this, "index", viewModel). Eu acho que é bobo que não haja uma maneira mais fácil de simplesmente invocar o mecanismo de exibição e obter uma string, mas no ASP.NET o mecanismo de exibição precisa fazer referência à instância do controlador para compilar o modo de exibição. Agora sou um desenvolvedor nodeJS e os motores de visualização no node são módulos separados inteiros que você pode invocar manualmente ou com uma estrutura MVC como express .
Chev
5
@PauloManuelSantos Eu concordo com você. Escrevi isso anos atrás e não sei o que estava pensando ao incluir o tratamento de erros na resposta. Eu atualizei a resposta para excluir o try / catch. Obrigado pelo feedback.
Chev
4

A resposta aceita por @Chev acima é boa, mas eu queria renderizar o resultado de uma ação específica , não apenas de uma determinada visualização .

Além disso, eu precisava ser capaz de passar parâmetros para essa ação em vez de confiar na injeção de um modelo.

Então eu vim com meu próprio método, que coloquei na classe base dos meus controladores (tornando-o disponível para todos eles):

    protected string RenderViewResultAsString(ViewResult viewResult)
    {
        using (var stringWriter = new StringWriter())
        {
            this.RenderViewResult(viewResult, stringWriter);

            return stringWriter.ToString();
        }
    }

    protected void RenderViewResult(ViewResult viewResult, TextWriter textWriter)
    {
        var viewEngineResult = this.ViewEngineCollection.FindView(
            this.ControllerContext, 
            viewResult.ViewName, 
            viewResult.MasterName);
        var view = viewEngineResult.View;

        try
        {
            var viewContext = new ViewContext(
                this.ControllerContext, 
                view, 
                this.ViewData, 
                this.TempData, 
                textWriter);

            view.Render(viewContext, textWriter);
        }
        finally
        {
            viewEngineResult.ViewEngine.ReleaseView(this.ControllerContext, view);
        }
    }

Suponha que eu tenha uma ação chamada Fooque leva um objeto de modelo e alguns outros parâmetros, que juntos influenciam qual visualização será usada:

    public ViewResult Foo(MyModel model, int bar)
    {
        if (bar == 1)
            return this.View("Bar1");
        else
            return this.View("Bar2", model);
    }

Agora, se eu quiser obter o resultado da ação de chamada Foo, posso simplesmente obter o ViewResultinvocando o Foométodo e, em seguida, chamar RenderViewResultAsStringpara obter o texto HTML:

    var viewResult = this.Foo(model, bar);

    var html = this.RenderViewResultAsString(viewResult);
Gary McGill
fonte