Come eseguire il rendering di una vista MVC ASP.NET come stringa?


485

Voglio generare due diverse visualizzazioni (una come stringa che verrà inviata come e-mail) e l'altra la pagina visualizzata a un utente.

È possibile in ASP.NET MVC beta?

Ho provato più esempi:

1. RenderPartial su String in ASP.NET MVC Beta

Se uso questo esempio, ricevo il messaggio "Impossibile reindirizzare dopo che le intestazioni HTTP sono state inviate".

2. MVC Framework: acquisizione dell'output di una vista

Se lo uso, mi sembra di non essere in grado di eseguire un reindirizzamento ToAction, poiché tenta di eseguire il rendering di una vista che potrebbe non esistere. Se restituisco la vista, è completamente incasinata e non sembra affatto corretta.

Qualcuno ha idee / soluzioni a questi problemi che ho o ho suggerimenti per quelli migliori?

Grazie molto!

Di seguito è riportato un esempio. Quello che sto cercando di fare è creare il metodo 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);
}

Risposta accettata da Tim Scott (modificata e formattata un po 'da me):

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

Esempio di utilizzo

Supponendo una chiamata dal controller per ottenere l'e-mail di conferma dell'ordine, passando la posizione Site.Master.

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

2
Come puoi usarlo con una vista fortemente tipizzata? Vale a dire. come posso alimentare un modello nella pagina?
Kjensen,

Non è possibile utilizzare questo e creare successivamente JsonResult, poiché il tipo di contenuto non può essere impostato dopo che le intestazioni sono state inviate (perché Flush le invia).
Arnis Lapsa,

Perché non esiste una sola risposta corretta, suppongo. :) Ho creato una domanda che era specifica per me, ma sapevo che sarebbe stata anche una domanda molto diffusa.
Dan Atkinson,

2
La soluzione suggerita non funziona in MVC 3.
Kasper Holdum

1
@Qua: la soluzione suggerita ha più di due anni. Non mi aspetto nemmeno che funzioni per MVC 3! Inoltre, ci sono modi migliori per farlo ora.
Dan Atkinson,

Risposte:


572

Ecco cosa mi è venuto in mente e funziona per me. Ho aggiunto i seguenti metodi alla mia classe base del controller. (Puoi sempre fare questi metodi statici da qualche altra parte che accettano un controller come parametro suppongo)

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

Rasoio .cshtml style

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

Modifica: aggiunto il codice Razor.


31
Il rendering di una vista in una stringa è sempre "incompatibile con l'intero concetto di routing", poiché non ha nulla a che fare con il routing. Non sono sicuro del motivo per cui una risposta che funziona ha ottenuto un voto negativo.
Ben Lesh,

4
Penso che potresti dover rimuovere "static" dalla dichiarazione del metodo della versione di Razor, altrimenti non troverà ControllerContext et al.
Mike,

3
Dovrai implementare il tuo metodo di rimozione per quegli spazi bianchi superflui. Il modo migliore che mi viene in mente dalla parte superiore della mia testa è di caricare la stringa in un XmlDocument, quindi di riscriverla in una stringa con un XmlWriter, come per il link che ho lasciato nel mio ultimo commento. Spero davvero che ti aiuti.
Ben Lesh,

3
Hmm come dovrei farlo usando un controller WebApi, ogni suggerimento sarebbe apprezzato
Alexander

3
Ciao a tutti per usarlo con la parola chiave "Static" per tutti i controller per renderlo comune devi creare una classe statica e al suo interno devi mettere questo metodo con "this" come parametro su "ControllerContext". Puoi vedere qui stackoverflow.com/a/18978036/2318354 esso.
Dilip0165,

68

Questa risposta non è sulla mia strada. Questo è originariamente da https://stackoverflow.com/a/2759898/2318354 ma qui ho mostrato il modo di usarlo con la parola chiave "statica" per renderlo comune a tutti i controller.

Per questo devi creare un staticfile class in class. (Supponiamo che il nome del tuo file di classe sia Utils.cs)

Questo esempio è per 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();
        }
    }
}

Ora puoi chiamare questa classe dal tuo controller aggiungendo NameSpace nel tuo File Controller come segue passando "this" come parametro al Controller.

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

Come suggerimento dato da @Sergey questo metodo di estensione può anche chiamare dal cotroller come indicato di seguito

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

Spero che questo ti sia utile per rendere il codice pulito e ordinato.


1
Bella soluzione! Una cosa, RenderRazorViewToString è in realtà un metodo di estensione (perché si passa il parametro del controller con questa parola chiave), quindi questo metodo di estensione può essere chiamato in questo modo: this.RenderRazorViewToString ("ViewName", modello);
Sergey,

@Sergey Hmmm ... Fammi controllare in questo modo, se va bene, aggiornerò la mia risposta. Comunque grazie per il tuo suggerimento.
Dilip0165,

Dilip0165, ho riscontrato un errore di riferimento null su var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName) ;. Hai qualche idea?
CB4,

@ CB4 Penso che potrebbe essere il problema di "viewName" che stai passando in funzione. Devi passare "viewName" con il percorso completo secondo la struttura della cartella. Quindi dai un'occhiata a questa cosa.
Dilip0165,

1
@Sergey Grazie per il tuo suggerimento, ho aggiornato la mia risposta secondo il tuo suggerimento, che è totalmente corretta
Dilip0165

32

Questo funziona per me:

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

Grazie per il tuo commento, ma non è quello usato per il rendering all'interno di una vista? Come potrei usarlo nel contesto in cui ho aggiornato la domanda?
Dan Atkinson,

Scusate, sto ancora pensando a Silverlight l'anno scorso, il cui primo rc è stato 0. :) Oggi ci sto provando. (Non appena ho elaborato il formato corretto del percorso di visualizzazione)
NikolaiDante,

Ciò interrompe ancora i reindirizzamenti in RC1
sconfitto il

sconfitto: no, non lo fa. Se lo fa, allora stai facendo qualcosa di sbagliato.
Dan Atkinson,

Fusione questo con stackoverflow.com/questions/520863/... , ha aggiunto la consapevolezza di ViewEnginesCollection, cercato di pop PartialView e ottenuto questo stackoverflow.com/questions/520863/... . : E
Arnis Lapsa,

31

Ho trovato una nuova soluzione che esegue il rendering di una vista su stringa senza dover fare confusione con il flusso di risposta dell'HttpContext corrente (che non consente di modificare il ContentType della risposta o altre intestazioni).

Fondamentalmente, tutto ciò che fai è creare un falso HttpContext affinché la vista si mostri:

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

Funziona su ASP.NET MVC 1.0, insieme a ContentResult, JsonResult, ecc. (La modifica delle intestazioni sull'HttpResponse originale non genera l' eccezione "Il server non può impostare il tipo di contenuto dopo che le intestazioni HTTP sono state inviate ").

Aggiornamento: in ASP.NET MVC 2.0 RC, il codice cambia un po 'perché dobbiamo passare l' StringWriterusato per scrivere la vista nel 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);

//...

Non esiste alcun metodo RenderPartial sull'oggetto HtmlHelper. Questo non è possibile - html.RenderPartial (viewName, viewData);
MartinF

1
Nella versione 1.0 di ASP.NET MVC ci sono un paio di metodi di estensione RenderPartial. Quello che sto usando in particolare è System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (questo HtmlHelper, stringa, oggetto). Non so se il metodo sia stato aggiunto nelle ultime revisioni di MVC e non fosse presente in quelle precedenti.
LorenzCK

Grazie. Ho solo bisogno di aggiungere lo spazio dei nomi System.Web.Mvc.Html alla dichiarazione using (altrimenti html.RenderPartial (..) ovviamente non sarà accessibile :))
MartinF

Qualcuno ha questo lavorando con RC di MVC2? Hanno aggiunto un parametro Textwriter aggiuntivo a ViewContext. Ho provato ad aggiungere un nuovo StringWriter (), ma non ha funzionato.
Becken,

1
@beckelmw: ho aggiornato la risposta. È necessario trasmettere l'originale che StringWritersi sta utilizzando per scrivere StringBuilder, non una nuova istanza o l'output della vista andrà perso.
LorenzCK,

11

Questo articolo descrive come eseguire il rendering di una vista in una stringa in diversi scenari:

  1. Controller MVC che chiama un altro dei propri ActionMethods
  2. Controller MVC che chiama un ActionMethod di un altro controller MVC
  3. Controller WebAPI che chiama un ActionMethod di un controller MVC

La soluzione / codice viene fornita come una classe chiamata ViewRenderer . Fa parte del WestwindToolkit di Rick Stahl presso GitHub .

Utilizzo (3. - Esempio WebAPI):

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

3
Anche come pacchetto NuGet West Wind Web MVC Utilities ( nuget.org/packages/Westwind.Web.Mvc ). Come bonus, il renderer della vista può non solo visualizzare viste parziali, ma anche l'intera vista incluso il layout. Articolo del blog con codice: weblog.west-wind.com/posts/2012/May/30/…
Jeroen K

Sarebbe bello se questo fosse suddiviso in pacchetti più piccoli. Il pacchetto Nuget apporta numerose modifiche al tuo web.config e aggiunge i file js al tuo progetto, che non vengono poi ripuliti quando lo disinstalli: /
Josh Noe,

8

Se vuoi rinunciare completamente a MVC, evitando così tutto il disordine di 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);

Questo utilizza il fantastico Razor Engine open source qui: https://github.com/Antaris/RazorEngine


Bello! Sai se esiste un motore di analisi simile per la sintassi di WebForms? Ho ancora alcune vecchie visualizzazioni WebForms che non possono ancora essere spostate su Razor.
Dan Atkinson,

Ciao, ho avuto molti problemi con il rasoio elettrico e la segnalazione degli errori non è molto buona. Non credo che l'helper Url sia supportato
Layinka,

@Layinka Non sono sicuro che ciò aiuti, ma la maggior parte delle informazioni sull'errore sono di CompilerErrorsproprietà dell'eccezione.
Josh Noe,

5

ottieni la vista in stringa usando questo modo

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

Questo metodo viene chiamato in due modi

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

O

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

4

Suggerimento aggiuntivo per ASP NET CORE:

Interfaccia:

public interface IViewRenderer
{
  Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model);
}

Implementazione:

public class ViewRenderer : IViewRenderer
{
  private readonly IRazorViewEngine viewEngine;

  public ViewRenderer(IRazorViewEngine viewEngine) => this.viewEngine = viewEngine;

  public async Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model)
  {
    ViewEngineResult viewEngineResult = this.viewEngine.FindView(controller.ControllerContext, name, false);

    if (!viewEngineResult.Success)
    {
      throw new InvalidOperationException(string.Format("Could not find view: {0}", name));
    }

    IView view = viewEngineResult.View;
    controller.ViewData.Model = model;

    await using var writer = new StringWriter();
    var viewContext = new ViewContext(
       controller.ControllerContext,
       view,
       controller.ViewData,
       controller.TempData,
       writer,
       new HtmlHelperOptions());

       await view.RenderAsync(viewContext);

       return writer.ToString();
  }
}

Registrazione in Startup.cs

...
 services.AddSingleton<IViewRenderer, ViewRenderer>();
...

E utilizzo nel controller:

public MyController: Controller
{
  private readonly IViewRenderer renderer;
  public MyController(IViewRendere renderer) => this.renderer = renderer;
  public async Task<IActionResult> MyViewTest
  {
    var view = await this.renderer.RenderAsync(this, "MyView", model);
    return new OkObjectResult(view);
  }
}

3

Sto usando MVC 1.0 RTM e nessuna delle soluzioni di cui sopra ha funzionato per me. Ma questo ha fatto:

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

2

Ho visto un'implementazione per MVC 3 e Razor da un altro sito Web, ha funzionato per me:

    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;

        }

    }

Altro su Razor render- MVC3 Visualizza il rendering in stringa


Sì, questa è in realtà più o meno una copia della risposta accettata. :)
Dan Atkinson,

2

Per eseguire il rendering di una vista in una stringa nel livello di servizio senza dover passare ControllerContext, c'è un buon articolo di Rick Strahl qui http://www.codemag.com/Article/1312081 che crea un controller generico. Riepilogo codice di seguito:

// Some Static Class
public static string RenderViewToString(ControllerContext context, string viewPath, object model = null, bool partial = false)
{
    // first find the ViewEngine for this view
    ViewEngineResult viewEngineResult = null;
    if (partial)
        viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
    else
        viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);

    if (viewEngineResult == null)
        throw new FileNotFoundException("View cannot be found.");

    // get the view and attach the model to view data
    var view = viewEngineResult.View;
    context.Controller.ViewData.Model = model;

    string result = null;

    using (var sw = new StringWriter())
    {
        var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
        view.Render(ctx, sw);
        result = sw.ToString();
    }

    return result;
}

// In the Service Class
public class GenericController : Controller
{ }

public static T CreateController<T>(RouteData routeData = null) where T : Controller, new()
{
    // create a disconnected controller instance
    T controller = new T();

    // get context wrapper from HttpContext if available
    HttpContextBase wrapper;
    if (System.Web.HttpContext.Current != null)
        wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
    else
        throw new InvalidOperationException("Cannot create Controller Context if no active HttpContext instance is available.");

    if (routeData == null)
        routeData = new RouteData();

    // add the controller routing if not existing
    if (!routeData.Values.ContainsKey("controller") &&
        !routeData.Values.ContainsKey("Controller"))
        routeData.Values.Add("controller", controller.GetType().Name.ToLower().Replace("controller", ""));

    controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
    return controller;
}

Quindi per eseguire il rendering della vista nella classe Servizio:

var stringView = RenderViewToString(CreateController<GenericController>().ControllerContext, "~/Path/To/View/Location/_viewName.cshtml", theViewModel, true);

1

Suggerimento veloce

Per un modello fortemente tipizzato basta aggiungerlo alla proprietà ViewData.Model prima di passare a RenderViewToString. per esempio

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

0

Per ripetere una domanda più sconosciuta, dai un'occhiata a MvcIntegrationTestFramework .

Ti consente di scrivere i tuoi aiutanti per lo streaming dei risultati e ha dimostrato di funzionare abbastanza bene. Suppongo che questo sarebbe in un progetto di test e come bonus avresti le altre capacità di test una volta ottenuta questa configurazione. Il fastidio principale sarebbe probabilmente risolvere la catena delle dipendenze.

 private static readonly string mvcAppPath = 
     Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory 
     + "\\..\\..\\..\\MyMvcApplication");
 private readonly AppHost appHost = new AppHost(mvcAppPath);

    [Test]
    public void Root_Url_Renders_Index_View()
    {
        appHost.SimulateBrowsingSession(browsingSession => {
            RequestResult result = browsingSession.ProcessRequest("");
            Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
        });
}

0

Ecco una classe che ho scritto per fare questo per ASP.NETCore RC2. Lo uso in modo da poter generare e-mail html utilizzando Razor.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System.IO;
using System.Threading.Tasks;

namespace cloudscribe.Web.Common.Razor
{
    /// <summary>
    /// the goal of this class is to provide an easy way to produce an html string using 
    /// Razor templates and models, for use in generating html email.
    /// </summary>
    public class ViewRenderer
    {
        public ViewRenderer(
            ICompositeViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IHttpContextAccessor contextAccesor)
        {
            this.viewEngine = viewEngine;
            this.tempDataProvider = tempDataProvider;
            this.contextAccesor = contextAccesor;
        }

        private ICompositeViewEngine viewEngine;
        private ITempDataProvider tempDataProvider;
        private IHttpContextAccessor contextAccesor;

        public async Task<string> RenderViewAsString<TModel>(string viewName, TModel model)
        {

            var viewData = new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
            {
                Model = model
            };

            var actionContext = new ActionContext(contextAccesor.HttpContext, new RouteData(), new ActionDescriptor());
            var tempData = new TempDataDictionary(contextAccesor.HttpContext, tempDataProvider);

            using (StringWriter output = new StringWriter())
            {

                ViewEngineResult viewResult = viewEngine.FindView(actionContext, viewName, true);

                ViewContext viewContext = new ViewContext(
                    actionContext,
                    viewResult.View,
                    viewData,
                    tempData,
                    output,
                    new HtmlHelperOptions()
                );

                await viewResult.View.RenderAsync(viewContext);

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

0

Ho trovato un modo migliore per eseguire il rendering della pagina di visualizzazione del rasoio quando ho riscontrato un errore con i metodi sopra, questa soluzione sia per l'ambiente web form che per l'ambiente mvc. Non è necessario alcun controller.

Ecco l'esempio di codice, in questo esempio ho simulato un'azione mvc con un gestore http asincrono:

    /// <summary>
    /// Enables processing of HTTP Web requests asynchronously by a custom HttpHandler that implements the IHttpHandler interface.
    /// </summary>
    /// <param name="context">An HttpContext object that provides references to the intrinsic server objects.</param>
    /// <returns>The task to complete the http request.</returns>
    protected override async Task ProcessRequestAsync(HttpContext context)
    {
        if (this._view == null)
        {
            this.OnError(context, new FileNotFoundException("Can not find the mvc view file.".Localize()));
            return;
        }
        object model = await this.LoadModelAsync(context);
        WebPageBase page = WebPageBase.CreateInstanceFromVirtualPath(this._view.VirtualPath);
        using (StringWriter sw = new StringWriter())
        {
            page.ExecutePageHierarchy(new WebPageContext(new HttpContextWrapper(context), page, model), sw);
            await context.Response.Output.WriteAsync(sw.GetStringBuilder().ToString());
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.