512 Stimmen

Wie wird eine ASP.NET MVC-Ansicht als Zeichenfolge gerendert?

Ich möchte zwei verschiedene Ansichten ausgeben (eine als Zeichenkette, die als E-Mail gesendet wird), und die andere die Seite, die einem Benutzer angezeigt wird.

Ist dies in ASP.NET MVC Beta möglich?

Ich habe mehrere Beispiele ausprobiert:

1. RenderPartial zu String in ASP.NET MVC Beta

Wenn ich dieses Beispiel verwende, erhalte ich die Meldung "Cannot redirect after HTTP Header gesendet wurden.".

2. MVC-Framework: Erfassen der Ausgabe einer Ansicht

Wenn ich dies verwende, scheine ich nicht in der Lage zu sein, eine redirectToAction zu machen, da es versucht, eine Ansicht zu rendern, die möglicherweise nicht existiert. Wenn ich die Ansicht zurückgebe, ist sie ist völlig durcheinander und sieht überhaupt nicht richtig.

Hat jemand Ideen/Lösungen für diese Probleme, die ich habe, oder hat Vorschläge für bessere Lösungen?

Vielen Dank!

Nachstehend finden Sie ein Beispiel. Was ich versuche, ist die Erstellung der GetViewForEmail-Methode :

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

Akzeptierte Antwort von Tim Scott (von mir ein wenig geändert und formatiert):

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

Beispiel für die Verwendung

Angenommen, der Controller ruft die E-Mail zur Bestellbestätigung auf und übergibt den Standort des Site.Master.

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

592voto

Ben Lesh Punkte 106494

Hier ist, was ich kam mit, und es ist für mich arbeiten. Ich habe die folgende(n) Methode(n) zu meiner Controller-Basisklasse hinzugefügt. (Sie können diese statischen Methoden immer irgendwo anders machen, die einen Controller als Parameter akzeptieren, nehme ich an)

MVC2 .ascx Stil

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 .cshtml-Stil

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

Bearbeiten: Razor-Code hinzugefügt.

72voto

Dilip0165 Punkte 4277

Diese Antwort liegt nicht auf meinem Weg. Dies ist ursprünglich von https://stackoverflow.com/a/2759898/2318354 aber hier habe ich gezeigt, wie man es mit dem "Static"-Schlüsselwort verwenden kann, um es für alle Controller gemeinsam zu machen.

Hierfür müssen Sie static Klasse in der Klassendatei . (Angenommen, der Name Ihrer Klassendatei lautet Utils.cs)

Dieses Beispiel ist für 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();
        }
    }
}

Jetzt können Sie diese Klasse von Ihrem Controller aufrufen, indem Sie NameSpace in Ihrer Controller-Datei wie folgt hinzufügen, indem Sie "this" als Parameter an Controller übergeben.

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

Wie von @Sergey vorgeschlagen, kann diese Erweiterungsmethode auch von cotroller wie folgt aufgerufen werden

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

Ich hoffe, dass dies für Sie nützlich sein wird, um den Code sauber und ordentlich zu machen.

34voto

Tim Scott Punkte 14679

Das funktioniert bei mir:

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

32voto

LorenzCK Punkte 7293

Ich fand eine neue Lösung, die eine Ansicht in Zeichenfolge rendert, ohne mit dem Response-Stream des aktuellen HttpContext (die nicht zulässt, dass Sie den ContentType oder andere Header der Antwort ändern) zu verwirren.

Im Grunde ist alles, was Sie tun, erstellen Sie eine gefälschte HttpContext für die Ansicht selbst zu rendern:

/// <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
}

Dies funktioniert auf ASP.NET MVC 1.0, zusammen mit ContentResult, JsonResult, etc. (Ändern Header auf der ursprünglichen HttpResponse nicht werfen die " Server kann den Inhaltstyp nicht festlegen, nachdem HTTP-Header gesendet wurden Ausnahme").

Aktualisierung: in ASP.NET MVC 2.0 RC ändert sich der Code ein wenig, da wir die StringWriter verwendet, um die Ansicht in die Datei 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);

//...

14voto

Krisztián Balla Punkte 17464

Dieser Artikel beschreibt, wie man eine Ansicht in verschiedenen Szenarien in eine Zeichenkette umwandelt:

  1. MVC-Controller, der eine andere seiner eigenen ActionMethods aufruft
  2. MVC-Controller, der eine ActionMethod eines anderen MVC-Controllers aufruft
  3. WebAPI-Controller ruft eine ActionMethod eines MVC-Controllers auf

Die Lösung/der Code wird in Form einer Klasse namens ViewRenderer . Sie ist Teil von Rick Stahls WestwindToolkit auf GitHub .

Verwendung (3. - WebAPI-Beispiel):

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X