web-dev-qa-db-fra.com

MVC 5 Render View to String

Il semble que la plupart du code de rendu de vue en chaîne ne fonctionne pas dans MVC 5.

J'ai les derniers modèles MVC 5.1.2 et j'essaie de rendre la vue en chaîne.

    public static String RenderViewToString(ControllerContext context, String viewPath, object model = null)
    {
        context.Controller.ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindView(context, viewPath, null);
            var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(context, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }

Eh bien, cela fonctionne mais sa sortie contient beaucoup de marques $ à la place de balises. J'ai lu quelque chose à ce sujet qui a été corrigé dans la version RC, mais c'est une vieille nouvelle.

Le problème ressemble à ceci

<$A$><h1></h1> 
<table</$A$><$B$> class=""</$B$><$C$>> <tbody</$C$><$D$></$D$><$E$>></tbody>
</table></$E$>

Je voudrais demander comment vous rendez les vues en chaîne dans le dernier modèle MVC 5? Merci.

17
wh1sp3r

Ok, semble avoir trouvé une solution. L'auteur de l'idée est le Manoir Yakir.

class FakeController : ControllerBase
{
    protected override void ExecuteCore() { }
    public static string RenderViewToString(string controllerName, string viewName, object viewData)
    {
        using (var writer = new StringWriter())
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", controllerName);
            var fakeControllerContext = new ControllerContext(new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null), new HttpResponse(null))), routeData, new FakeController());
            var razorViewEngine = new RazorViewEngine();
            var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);

            var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);
            razorViewResult.View.Render(viewContext, writer);
            return writer.ToString();

        }
    }
}

C'est une astuce avec un contexte et une réponse factices.

Exemple:

String renderedHTML = RenderViewToString("Email", "MyHTMLView", myModel );

Mon fichier MyHTMLView.cstml est stocké dans Views/Email/MyHTMLView.cshtml. Email est un faux nom de contrôleur.

27
wh1sp3r

voici la solution qui fonctionne avec la session et les zones sur MVC5.

public class FakeController : ControllerBase
{
    protected override void ExecuteCore() { }
    public static string RenderViewToString(string controllerName, string viewName,string areaName, object viewData,RequestContext rctx)
    {
        try
        {
            using (var writer = new StringWriter())
            {
                var routeData = new RouteData();
                routeData.Values.Add("controller", controllerName);
                routeData.Values.Add("Area", areaName);
                routeData.DataTokens["area"] = areaName;

                var fakeControllerContext = new ControllerContext(rctx, new FakeController());
                //new ControllerContext(new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null), new HttpResponse(null))), routeData, new FakeController());
                fakeControllerContext.RouteData = routeData;

                var razorViewEngine = new RazorViewEngine();

                var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);

                var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);

                razorViewResult.View.Render(viewContext, writer);
                return writer.GetStringBuilder().ToString();
                //use example
                //String renderedHTML = RenderViewToString("Email", "MyHTMLView", myModel );
                //where file MyHTMLView.cstml is stored in Views/Email/MyHTMLView.cshtml. Email is a fake controller name.
            }
        }
        catch (Exception ex)
        {
            //do your exception handling here
        }
    }
}

voici comment vous appelez cela d'un autre contrôleur

var modal = getModal(params);
return FakeController.RenderViewToString(controllerName, viewName, areaName, modal, this.Request.RequestContext);

en utilisant requestcontext, nous pouvons facilement passer la session en cours dans fakecontroller et afficher une chaîne de rasoir. 

4
Alok

J'ai immédiatement eu besoin de renvoyer 6 vues partielles sous forme de chaînes dans un objet JSON. Au lieu de créer une méthode statique et de transmettre tous les paramètres inutiles, j'ai décidé d'ajouter des méthodes protégées à notre classe ControllerBase dérivée de Controller et utilisée comme classe de base pour tous nos contrôleurs.

Voici une classe ControllerBase entièrement fonctionnelle qui fournit cette fonctionnalité et fonctionne de manière très similaire aux méthodes PartialView () et View () qui figurent dans la classe Controller. Il inclut les ajouts de @Alok.

public abstract class ControllerBase : Controller
{
    #region PartialViewToString

    protected string PartialViewToString(string partialViewName, object model = null)
    {
        ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);

        return ViewToString(
            controllerContext,
            ViewEngines.Engines.FindPartialView(controllerContext, partialViewName) ?? throw new FileNotFoundException("Partial view cannot be found."),
            model
        );
    }

    #endregion

    #region ViewToString

    protected string ViewToString(string viewName, object model = null)
    {
        ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);

        return ViewToString(
            controllerContext,
            ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
            model
        );
    }

    protected string ViewToString(string viewName, string controllerName, string areaName, object model = null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", controllerName);

        if (areaName != null)
        {
            routeData.Values.Add("Area", areaName);
            routeData.DataTokens["area"] = areaName;
        }

        ControllerContext controllerContext = new ControllerContext(HttpContext, routeData, this);

        return ViewToString(
            controllerContext,
            ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
            model
        );
    }

    #endregion

    #region Private Methods

    private string ViewToString(ControllerContext controllerContext, ViewEngineResult viewEngineResult, object model)
    {
        using (StringWriter writer = new StringWriter())
        {
            ViewContext viewContext = new ViewContext(
                ControllerContext,
                viewEngineResult.View,
                new ViewDataDictionary(model),
                new TempDataDictionary(),
                writer
            );

            viewEngineResult.View.Render(viewContext, writer);

            return writer.ToString();
        }
    }

    #endregion
}
1
William Bosacker

@ wh1sp3r answer fonctionne, mais pour les vues partielles, j'ai dû effectuer les modifications mineures suivantes:

 string html = FakeController.RenderViewToString("**Controllername**", "~/views/**Controllername**/_AsdfPartialPage.cshtml", fem);


public static string RenderViewToString(string controllerName, string viewName, object viewData)
    {
        using (var writer = new StringWriter())
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", controllerName);
            var fakeControllerContext = new ControllerContext(new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://localhost", null), new HttpResponse(null))), routeData, new FakeController());
            var razorViewEngine = new RazorViewEngine();
            //var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);

            var razorViewResult = razorViewEngine.FindPartialView(fakeControllerContext, viewName,  false);

            var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);
            razorViewResult.View.Render(viewContext, writer);
            return writer.ToString();

        }
    }
0
stefan