Question Comment rendre une vue ASP.NET MVC sous forme de chaîne?


Je veux afficher deux vues différentes (une en tant que chaîne qui sera envoyée en tant que courrier électronique), et l'autre la page affichée à un utilisateur.

Est-ce possible dans ASP.NET MVC beta?

J'ai essayé plusieurs exemples:

1.  RenderPartial à String dans ASP.NET MVC Beta

Si j'utilise cet exemple, je reçois le "Impossible de rediriger après HTTP   les en-têtes ont été envoyés. "

2.  MVC Framework: capture de la sortie d'une vue

Si je l'utilise, il me semble être incapable de faire un redirectToAction, car il   essaie de rendre une vue qui peut ne pas exister. Si je retourne la vue, il   est complètement foiré et ne semble pas correct du tout.

Est-ce que quelqu'un a des idées / solutions à ces problèmes ou a-t-il des suggestions pour les meilleures?

Merci beaucoup!

Voici un exemple. Ce que j'essaie de faire est de créer le Méthode GetViewForEmail:

public ActionResult OrderResult(string ref)
{
    //Get the order
    Order order = OrderService.GetOrder(ref);

    //The email helper would do the meat and veg by getting the view as a string
    //Pass the control name (OrderResultEmail) and the model (order)
    string emailView = GetViewForEmail("OrderResultEmail", order);

    //Email the order out
    EmailHelper(order, emailView);
    return View("OrderResult", order);
}

Réponse acceptée de Tim Scott (changée et formatée un peu par moi):

public virtual string RenderViewToString(
    ControllerContext controllerContext,
    string viewPath,
    string masterPath,
    ViewDataDictionary viewData,
    TempDataDictionary tempData)
{
    Stream filter = null;
    ViewPage viewPage = new ViewPage();

    //Right, create our view
    viewPage.ViewContext = new ViewContext(controllerContext, new WebFormView(viewPath, masterPath), viewData, tempData);

    //Get the response context, flush it and get the response filter.
    var response = viewPage.ViewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;

    try
    {
        //Put a new filter into the response
        filter = new MemoryStream();
        response.Filter = filter;

        //Now render the view into the memorystream and flush the response
        viewPage.ViewContext.View.Render(viewPage.ViewContext, viewPage.ViewContext.HttpContext.Response.Output);
        response.Flush();

        //Now read the rendered view.
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        //Clean up.
        if (filter != null)
        {
            filter.Dispose();
        }

        //Now replace the response filter
        response.Filter = oldFilter;
    }
}

Exemple d'utilisation

En supposant un appel du contrôleur pour recevoir l'e-mail de confirmation de la commande, en passant l'emplacement Site.Master.

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);

428


origine


Réponses:


Voici ce que j'ai trouvé, et ça marche pour moi. J'ai ajouté la ou les méthodes suivantes à ma classe de base de contrôleur. (Vous pouvez toujours faire ces méthodes statiques à un autre endroit qui accepte un contrôleur en tant que paramètre, je suppose)

MVC2 .ascx style

protected string RenderViewToString<T>(string viewPath, T model) {
  ViewData.Model = model;
  using (var writer = new StringWriter()) {
    var view = new WebFormView(ControllerContext, viewPath);
    var vdd = new ViewDataDictionary<T>(model);
    var viewCxt = new ViewContext(ControllerContext, view, vdd,
                                new TempDataDictionary(), writer);
    viewCxt.View.Render(viewCxt, writer);
    return writer.ToString();
  }
}

Razor style .cshtml

public string RenderRazorViewToString(string viewName, object model)
{
  ViewData.Model = model;
  using (var sw = new StringWriter())
  {
    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                             viewName);
    var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                 ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);
    viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

Edit: ajouté le code Razor.


513



Cette réponse n'est pas sur mon chemin. Ceci est originaire de https://stackoverflow.com/a/2759898/2318354  mais ici, j'ai montré la façon de l'utiliser avec le mot-clé "statique" pour le rendre commun pour tous les contrôleurs.

Pour cela, vous devez faire static classe dans le fichier de classe. (Supposons que votre nom de fichier de classe soit Utils.cs)

Cet exemple est For Razor.

Utils.cs

public static class RazorViewToString
{
    public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
    {
        controller.ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
}

Vous pouvez maintenant appeler cette classe à partir de votre contrôleur en ajoutant NameSpace dans votre fichier de contrôleur de la manière suivante en transmettant "this" en tant que paramètre à Controller.

string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);

Comme suggéré par @Sergey cette méthode d'extension peut aussi appeler depuis cotroller comme indiqué ci-dessous

string result = this.RenderRazorViewToString("ViewName", model);

J'espère que cela vous sera utile pour rendre le code propre et net.


50



Cela fonctionne pour moi:

public virtual string RenderView(ViewContext viewContext)
{
    var response = viewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;
    Stream filter = null;
    try
    {
        filter = new MemoryStream();
        response.Filter = filter;
        viewContext.View.Render(viewContext, viewContext.HttpContext.Response.Output);
        response.Flush();
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        if (filter != null)
        {
            filter.Dispose();
        }
        response.Filter = oldFilter;
    }
}

30



J'ai trouvé une nouvelle solution qui rend une vue à la chaîne sans avoir à manipuler le flux de réponse du HttpContext actuel (ce qui ne vous permet pas de modifier le ContentType de la réponse ou d'autres en-têtes).

Fondamentalement, tout ce que vous faites est de créer un faux HttpContext pour que la vue se rende:

/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller,
                                        string viewName, object viewData) {
    //Create memory writer
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    //Create fake http context to render the view
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
    var fakeControllerContext = new ControllerContext(
        new HttpContextWrapper(fakeContext),
        controller.ControllerContext.RouteData,
        controller.ControllerContext.Controller);

    var oldContext = HttpContext.Current;
    HttpContext.Current = fakeContext;

    //Use HtmlHelper to render partial view to fake context
    var html = new HtmlHelper(new ViewContext(fakeControllerContext,
        new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
        new ViewPage());
    html.RenderPartial(viewName, viewData);

    //Restore context
    HttpContext.Current = oldContext;    

    //Flush memory and return output
    memWriter.Flush();
    return sb.ToString();
}

/// <summary>Fake IView implementation used to instantiate an HtmlHelper.</summary>
public class FakeView : IView {
    #region IView Members

    public void Render(ViewContext viewContext, System.IO.TextWriter writer) {
        throw new NotImplementedException();
    }

    #endregion
}

Cela fonctionne sur ASP.NET MVC 1.0, avec ContentResult, JsonResult, etc. (changer les en-têtes sur le HttpResponse original ne lance pas le "Le serveur ne peut pas définir le type de contenu après l'envoi des en-têtes HTTP"exception".

Mettre à jour: dans ASP.NET MVC 2.0 RC, le code change un peu parce que nous devons passer dans le StringWriter utilisé pour écrire la vue dans le ViewContext:

//...

//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(
    new ViewContext(fakeControllerContext, new FakeView(),
        new ViewDataDictionary(), new TempDataDictionary(), memWriter),
    new ViewPage());
html.RenderPartial(viewName, viewData);

//...

28



Cet article décrit comment rendre une vue à une chaîne dans différents scénarios:

  1. MVC Controller appelant un autre de ses propres ActionMethods
  2. Contrôleur MVC appelant une méthode ActionMethod d'un autre contrôleur MVC
  3. Contrôleur WebAPI appelant une méthode d'action d'un contrôleur MVC

La solution / code est fournie en tant que classe appelée ViewRenderer. Cela fait partie de Rick Stahl WestwindToolkit à GitHub.

Usage (3. - Exemple WebAPI):

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));

10



Si vous voulez renoncer complètement à MVC, évitant ainsi tout le désordre HttpContext ...

using RazorEngine;
using RazorEngine.Templating; // For extension methods.

string razorText = System.IO.File.ReadAllText(razorTemplateFileLocation);
string emailBody = Engine.Razor.RunCompile(razorText, "templateKey", typeof(Model), model);

Ceci utilise l'impressionnant Razor Engine open source ici: https://github.com/Antaris/RazorEngine


7



vous obtenez la vue en chaîne en utilisant cette façon

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    if (model != null)
        ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

Nous appelons cette méthode de deux façons

string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", null)

OU

var model = new Person()
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)

4



J'utilise MVC 1.0 RTM et aucune des solutions ci-dessus n'a fonctionné pour moi. Mais celui-ci a fait:

Public Function RenderView(ByVal viewContext As ViewContext) As String

    Dim html As String = ""

    Dim response As HttpResponse = HttpContext.Current.Response

    Using tempWriter As New System.IO.StringWriter()

        Dim privateMethod As MethodInfo = response.GetType().GetMethod("SwitchWriter", BindingFlags.NonPublic Or BindingFlags.Instance)

        Dim currentWriter As Object = privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {tempWriter}, Nothing)

        Try
            viewContext.View.Render(viewContext, Nothing)
            html = tempWriter.ToString()
        Finally
            privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {currentWriter}, Nothing)
        End Try

    End Using

    Return html

End Function

3



J'ai vu une implémentation pour MVC 3 et Razor d'un autre site web, ça a marché pour moi:

    public static string RazorRender(Controller context, string DefaultAction)
    {
        string Cache = string.Empty;
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        System.IO.TextWriter tw = new System.IO.StringWriter(sb); 

        RazorView view_ = new RazorView(context.ControllerContext, DefaultAction, null, false, null);
        view_.Render(new ViewContext(context.ControllerContext, view_, new ViewDataDictionary(), new TempDataDictionary(), tw), tw);

        Cache = sb.ToString(); 

        return Cache;

    } 

    public static string RenderRazorViewToString(string viewName, object model)
    {

        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            return sw.GetStringBuilder().ToString();
        }
    } 

    public static class HtmlHelperExtensions
    {
        public static string RenderPartialToString(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
        {
            ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);

            if (result.View != null)
            {
                StringBuilder sb = new StringBuilder();
                using (StringWriter sw = new StringWriter(sb))
                {
                    using (HtmlTextWriter output = new HtmlTextWriter(sw))
                    {
                        ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, output);
                        result.View.Render(viewContext, output);
                    }
                }
                return sb.ToString();
            } 

            return String.Empty;

        }

    }

Plus sur Razor render - MVC3 View Render to String


2