En MVC3 Razor, ¿cómo obtengo el html de una vista renderizada dentro de una acción?

90

¿Alguien sabe cómo obtener el html generado de una vista dentro de una acción?

Es algo como esto:

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

Respuestas:

152

Utilizo un método estático en una clase que llamé. Utilities.CommonLe paso las vistas al cliente como propiedades de los objetos JSON constantemente, así que tuve la necesidad de representarlas en una cadena. Aqui tienes:

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

Esto funcionará tanto para vistas completas como para vistas parciales, simplemente cambie ViewEngines.Engines.FindPartialViewa ViewEngines.Engines.FindView.

Chev
fuente
14
FindView necesita otro parámetro ( masterName) que especificaría null. También recomiendo guardar y restaurar (después de renderizar) controller.ViewData.Model en caso de que se llame al método en la instancia del controlador actual y el modelo se haya asignado antes de esta llamada.
Andrei Rînea
4
También debe llamar a viewResult.ViewEngine.ReleaseView (controller.ControllerContext, viewResult.View)
sjmeverett
3
Funciona muy bien, pero no me gustaría detectar y representar excepciones en mi código en vivo.
pauloya
3
Si lo llama desde el propio controlador, simplemente páselo thiscomo argumento del controlador. RenderPartialViewToString(this, "index", viewModel). Creo que es una tontería que no haya una manera más fácil de simplemente invocar el motor de vista y recuperar una cadena, pero en ASP.NET el motor de vista necesita referencia a la instancia del controlador para compilar la vista. Ahora soy desarrollador de nodeJS y los motores de vista en el nodo son módulos completamente separados que puede invocar manualmente o con un marco MVC como express .
Chev
5
@PauloManuelSantos Estoy de acuerdo contigo. Escribí esto hace años y no sé qué estaba pensando al incluir el manejo de errores en la respuesta. Actualicé la respuesta para excluir el try / catch. Gracias por la respuesta.
Chev
4

La respuesta aceptada por @Chev anterior es buena, pero quería representar el resultado de una acción específica , no solo una vista en particular .

Además, necesitaba poder pasar parámetros a esa acción en lugar de depender de inyectar un modelo.

Así que se me ocurrió mi propio método, que puse en la clase base de mis controladores (haciéndolo disponible para todos):

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

Supongamos que tengo una acción llamada Fooque toma un objeto modelo y algunos otros parámetros, que en conjunto influyen en la vista que se utilizará:

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

Ahora, si quiero obtener el resultado de llamar a la acción Foo, simplemente puedo obtenerlo ViewResultinvocando el Foométodo y luego llamar RenderViewResultAsStringpara obtener el texto HTML:

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

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