Jak w MVC3 Razor uzyskać kod HTML renderowanego widoku wewnątrz akcji?

90

Czy ktoś wie, jak uzyskać wygenerowany kod HTML widoku w akcji?

Czy to jest coś takiego:

public ActionResult Do()
{
    var html = RenderView("hello", model);
...
}
Omu
źródło

Odpowiedzi:

152

Używam metody statycznej w klasie, którą wywołałem. Utilities.CommonPrzekazuję widoki z powrotem do klienta jako właściwości obiektów JSON w sposób ciągły, więc musiałem renderować je w postaci ciągu. Proszę bardzo:

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

Będzie to działać zarówno w przypadku widoków pełnych, jak i częściowych, wystarczy zmienić ViewEngines.Engines.FindPartialViewna ViewEngines.Engines.FindView.

Chev
źródło
14
FindView wymaga innego parametru ( masterName), który należy określić jako null. Polecam również zapisanie i przywrócenie (po wyrenderowaniu) kontrolera.ViewData.Model w przypadku wywołania metody na bieżącej instancji kontrolera, a model został przypisany przed tym wywołaniem.
Andrei Rînea
4
Powinieneś także wywołać metodę viewResult.ViewEngine.ReleaseView (controller.ControllerContext, viewResult.View)
sjmeverett
3
Działa świetnie, ale nie chciałbym przechwytywać i renderować wyjątków w moim aktywnym kodzie.
pauloya,
3
Jeśli wywołujesz go z poziomu samego kontrolera, po prostu przekaż thisjako argument kontrolera. RenderPartialViewToString(this, "index", viewModel). Myślę, że to głupie, że nie ma prostszego sposobu, aby po prostu wywołać silnik widoku i odzyskać ciąg, ale w ASP.NET silnik widoku potrzebuje odniesienia do instancji kontrolera, aby skompilować widok. Jestem teraz programistą nodeJS i przeglądanie silników w węźle to całe oddzielne moduły, które można wywołać ręcznie lub za pomocą struktury MVC, takiej jak express .
Chev
5
@PauloManuelSantos Zgadzam się z tobą. Napisałem to lata temu i nie wiem, o czym myślałem, uwzględniając obsługę błędów w odpowiedzi. Zaktualizowałem odpowiedź, aby wykluczyć próbę / złapanie. Dzięki za opinie.
Chev
4

Przyjęta odpowiedź przez @Chev powyżej jest dobra, ale chciałem wyrenderować wynik określonego działania , a nie tylko określonego widoku .

Musiałem też móc przekazywać parametry do tej akcji, zamiast polegać na wstrzykiwaniu modelu.

Wymyśliłem więc własną metodę, którą umieściłem w klasie bazowej moich kontrolerów (udostępniając ją wszystkim):

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

Załóżmy, że mam akcję o nazwie, Fooktóra pobiera obiekt modelu i kilka innych parametrów, które razem wpływają na to, jaki widok zostanie użyty:

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

Teraz, jeśli chcę uzyskać wynik wywołania akcji Foo, mogę po prostu uzyskać ViewResult, wywołując Foometodę, a następnie wywołać, RenderViewResultAsStringaby uzyskać tekst HTML:

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

    var html = this.RenderViewResultAsString(viewResult);
Gary McGill
źródło