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

485

Je veux sortir deux vues différentes (une sous forme de chaîne qui sera envoyée par e-mail) et l'autre la page affichée à un utilisateur.

Est-ce possible dans ASP.NET MVC beta?

J'ai essayé plusieurs exemples:

1. RenderPartial to String dans ASP.NET MVC Beta

Si j'utilise cet exemple, je reçois le message "Impossible de rediriger après l'envoi des en-têtes HTTP".

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

Si j'utilise ceci, je semble être incapable de faire une redirectToAction, car il essaie de rendre une vue qui peut ne pas exister. Si je retourne la vue, elle est complètement foirée et ne semble pas du tout correcte.

Quelqu'un a-t-il des idées / solutions à ces problèmes ou ai-je des suggestions pour de meilleurs?

Merci beaucoup!

Voici un exemple. Ce que j'essaie de faire, c'est de créer la 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 (modifié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 obtenir l'e-mail de confirmation de commande, en passant l'emplacement Site.Master.

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
Dan Atkinson
la source
2
Comment pouvez-vous l'utiliser avec une vue fortement typée? C'est à dire. comment puis-je alimenter un modèle sur la page?
Kjensen
Impossible d'utiliser cela et de créer JsonResult par la suite, car le type de contenu ne peut pas être défini après l'envoi des en-têtes (car Flush les envoie).
Arnis Lapsa
Parce qu'il n'y a pas de bonne réponse, je suppose. :) J'ai créé une question qui m'était spécifique, mais je savais que ce serait aussi une question largement posée.
Dan Atkinson
2
La solution suggérée ne fonctionne pas dans MVC 3.
Kasper Holdum
1
@Qua: La solution suggérée a plus de deux ans. Je ne m'attendrais pas non plus à ce que cela fonctionne pour MVC 3! En outre, il existe de meilleures façons de le faire maintenant.
Dan Atkinson

Réponses:

572

Voici ce que j'ai trouvé et ça marche pour moi. J'ai ajouté les méthodes suivantes à ma classe de base de contrôleur. (Vous pouvez toujours créer ces méthodes statiques ailleurs qui acceptent un contrôleur comme paramètre, je suppose)

Style MVC2 .ascx

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

Edit: ajout du code Razor.

Ben Lesh
la source
31
Le rendu d'une vue à une chaîne est toujours "incohérent avec le concept de routage entier", car cela n'a rien à voir avec le routage. Je ne sais pas pourquoi une réponse qui fonctionne a obtenu un vote négatif.
Ben Lesh
4
Je pense que vous devrez peut-être supprimer le "statique" de la déclaration de méthode de la version Razor, sinon il ne trouvera pas ControllerContext et al.
Mike
3
Vous devrez implémenter votre propre méthode de suppression pour ces espaces blancs superflus. La meilleure façon de penser du haut de ma tête est de charger la chaîne dans un XmlDocument, puis de la réécrire dans une chaîne avec un XmlWriter, selon le lien que j'ai laissé dans mon dernier commentaire. J'espère vraiment que ça aide.
Ben Lesh
3
Hmm comment dois-je faire cela en utilisant un contrôleur WebApi, toutes les suggestions seraient appréciées
Alexander
3
Salut à tous de l'utiliser avec le mot-clé "Static" pour tous les contrôleurs pour le rendre commun, vous devez créer une classe statique et à l'intérieur, vous devez mettre cette méthode avec "this" comme paramètre dans "ControllerContext". Vous pouvez le voir ici stackoverflow.com/a/18978036/2318354 it.
Dilip0165
68

Cette réponse n'est pas sur mon chemin. C'est à l'origine de https://stackoverflow.com/a/2759898/2318354 mais ici j'ai montré comment l'utiliser avec le mot-clé "Static" pour le rendre commun à tous les contrôleurs.

Pour cela, vous devez créer une staticclasse dans un fichier de classe. (Supposons que votre nom de fichier de classe soit Utils.cs)

Cet exemple est pour 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 contrôleur comme suit en passant "this" comme paramètre au contrôleur.

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

Comme suggéré par @Sergey, cette méthode d'extension peut également 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 soigné.

Dilip0165
la source
1
Bonne solution! Une chose, RenderRazorViewToString est en fait une méthode d'extension (car vous passez le paramètre du contrôleur avec ce mot-clé), donc cette méthode d'extension peut être appelée de cette façon: this.RenderRazorViewToString ("ViewName", modèle);
Sergey
@Sergey Hmmm ... Permettez-moi de vérifier de cette façon, si tout va bien, je mettrai à jour ma réponse. Quoi qu'il en soit Merci pour votre suggestion.
Dilip0165
Dilip0165, j'ai eu une erreur de référence nulle sur var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName) ;. Avez-vous une idée?
CB4
@ CB4 Je pense que ce pourrait être le problème de "viewName" que vous passez en fonction. Vous devez passer "viewName" avec le chemin complet selon la structure de votre dossier. Alors vérifiez cette chose.
Dilip0165
1
@Sergey Merci pour votre suggestion, j'ai mis à jour ma réponse selon votre suggestion qui est totalement correcte
Dilip0165
32

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;
    }
}
Tim Scott
la source
Merci pour votre commentaire, mais n'est-ce pas utilisé pour le rendu dans une vue? Comment pourrais-je l'utiliser dans le contexte avec lequel j'ai mis à jour la question?
Dan Atkinson
Désolé, je pense toujours à Silverlight l'année dernière, dont le premier rc était 0. :) (Dès que je travaille sur le format correct du chemin de vue)
NikolaiDante
Cela rompt toujours les redirections dans RC1
vaincu le
vaincu: Non, ce n'est pas le cas. Si c'est le cas, alors vous faites quelque chose de mal.
Dan Atkinson
A fusionné cela avec stackoverflow.com/questions/520863/… , a augmenté la connaissance de ViewEnginesCollection, a essayé de faire apparaître une vue partielle et a obtenu ce stackoverflow.com/questions/520863/… . : E
Arnis Lapsa
31

J'ai trouvé une nouvelle solution qui rend une vue en chaîne sans avoir à jouer avec le flux de réponse du HttpContext actuel (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, ainsi que ContentResult, JsonResult, etc. (la modification des en-têtes sur la HttpResponse d'origine ne renvoie pas l' exception «Le serveur ne peut pas définir le type de contenu après l'envoi des en-têtes HTTP »).

Mise à jour: dans ASP.NET MVC 2.0 RC, le code change un peu car nous devons passer le StringWriterutilisé pour écrire la vue dans 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);

//...
LorenzCK
la source
Il n'y a pas de méthode RenderPartial sur l'objet HtmlHelper. Ce n'est pas possible - html.RenderPartial (viewName, viewData);
MartinF
1
Dans ASP.NET MVC version 1.0, il existe quelques méthodes d'extension RenderPartial. Celui que j'utilise en particulier est System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (ce HtmlHelper, chaîne, objet). Je ne sais pas si la méthode a été ajoutée dans les dernières révisions de MVC et n'était pas présente dans les précédentes.
LorenzCK
Merci. Juste besoin d'ajouter l'espace de noms System.Web.Mvc.Html à la déclaration using (sinon html.RenderPartial (..) ne sera bien sûr pas accessible :))
MartinF
Quelqu'un at-il ce travail avec le RC de MVC2? Ils ont ajouté un paramètre Textwriter supplémentaire à ViewContext. J'ai juste essayé d'ajouter un nouveau StringWriter (), mais cela n'a pas fonctionné.
Beckelmw
1
@beckelmw: J'ai mis à jour la réponse. Vous devez transmettre l'original que StringWritervous utilisez pour écrire dans le StringBuilder, pas une nouvelle instance sinon la sortie de la vue sera perdue.
LorenzCK
11

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

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

La solution / le code est fourni sous la forme d'une classe appelée ViewRenderer . Il fait partie du WestwindToolkit de Rick Stahl sur GitHub .

Utilisation (3. - exemple WebAPI):

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
Jenny O'Reilly
la source
3
Également en tant que package NuGet West Wind Web MVC Utilities ( nuget.org/packages/Westwind.Web.Mvc ). En prime, le rendu de vue peut non seulement rendre des vues partielles, mais également la vue entière, y compris la disposition. Article de blog avec le code: weblog.west-wind.com/posts/2012/May/30/…
Jeroen K
Ce serait formidable si cela était divisé en petits paquets. Le package Nuget apporte un tas de modifications à votre web.config et ajoute des fichiers js à votre projet, qui ne sont pas nettoyés lorsque vous le désinstallez: /
Josh Noe
8

Si vous voulez renoncer entièrement à 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);

Cela utilise le moteur de rasoir open source génial ici: https://github.com/Antaris/RazorEngine

Josh Noe
la source
Agréable! Savez-vous s'il existe un moteur d'analyse similaire pour la syntaxe WebForms? J'ai encore d'anciennes vues WebForms qui ne peuvent pas encore être déplacées vers Razor.
Dan Atkinson
Salut, j'ai eu beaucoup de problèmes avec le moteur de rasoir, et le rapport d'erreur n'est pas très agréable. Je ne pense pas que l'assistant Url soit pris en charge
Layinka
@Layinka Je ne sais pas si cela aide, mais la plupart des informations d'erreur se trouvent dans la CompilerErrorspropriété de l'exception.
Josh Noe
5

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 manières

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

OU

var model = new Person()
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)
Jayesh Patel
la source
4

Astuce supplémentaire pour ASP NET CORE:

Interface:

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

La mise en oeuvre:

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

Inscription dans Startup.cs

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

Et l'utilisation dans le contrôleur:

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);
  }
}
Marcin
la source
3

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
Jeremy Bell
la source
2

J'ai vu une implémentation pour MVC 3 et Razor à partir d'un autre site Web, cela a fonctionné 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;

        }

    }

En savoir plus sur Razor render - MVC3 View Render to String

Adamy
la source
Oui, c'est en fait plus ou moins une copie de la réponse acceptée. :)
Dan Atkinson
2

Pour rendre une vue à une chaîne dans la couche de service sans avoir à passer ControllerContext, il y a un bon article de Rick Strahl ici http://www.codemag.com/Article/1312081 qui crée un contrôleur générique. Résumé du code ci-dessous:

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

Ensuite, pour rendre la vue dans la classe Service:

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

Conseil rapide

Pour un modèle fortement typé, ajoutez-le simplement à la propriété ViewData.Model avant de passer à RenderViewToString. par exemple

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

Pour répéter à partir d'une question plus inconnue, jetez un œil à MvcIntegrationTestFramework .

Cela vous permet d'économiser en écrivant vos propres assistants pour diffuser les résultats et il est prouvé que cela fonctionne assez bien. Je suppose que ce serait dans un projet de test et en bonus, vous auriez les autres capacités de test une fois que vous aurez cette configuration. Le problème principal serait probablement de trier la chaîne de dépendance.

 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"));
        });
}
Colombe
la source
0

Voici une classe que j'ai écrite pour le faire pour ASP.NETCore RC2. Je l'utilise pour pouvoir générer des e-mails html à l'aide de 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();
            }
        }
    }
}
Joe Audette
la source
0

J'ai trouvé un meilleur moyen de rendre la page de vue du rasoir lorsque j'ai eu une erreur avec les méthodes ci-dessus, cette solution pour l'environnement de formulaire Web et l'environnement mvc. Aucun contrôleur n'est nécessaire.

Voici l'exemple de code, dans cet exemple, j'ai simulé une action mvc avec un gestionnaire http asynchrone:

    /// <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());
        }
    }
dexiang
la source