web-dev-qa-db-fra.com

Problèmes avec RazorEngine avec @Html

J'utilise RazorEngine pour afficher du contenu de base (un système de gestion de contenu très grossier).

Cela fonctionne très bien jusqu'à ce que j'inclue une syntaxe @Html dans le balisage.

Si le balisage contient un @html, j'obtiens l'erreur suivante:

Impossible de compiler le modèle. Le nom 'Html' n'existe pas dans le contexte actuel

Voici la vue qui rend le balisage:

@Model Models.ContentPage

@{
    ViewBag.Title = Model.MetaTitle;
    Layout = "~/Views/Shared/Templates/_" + Model.Layout + "Layout.cshtml";

}
@Html.Raw(RazorEngine.Razor.Parse(Model.Markup, Model))

J'ai vu sur le site Codeplex pour RazorEngine l'utilisation de @Html (je sais que la version là-bas est obsolète et j'ai obtenu ma version via nuget).

Toute aide à ce sujet serait formidable.

32
JamesStuddart

Les propriétés d'assistance Html et Url sont des fonctionnalités réelles de l'implémentation de MVC de Razor dans leur moteur de vue. Par défaut, Html et Url ne sont actuellement pas pris en charge sans spécialisation d'un modèle de base personnalisé.

La prochaine version v3 sera accompagnée d'une version associée de RazorEngine.Web, qui inclura, espérons-le, un modèle de base compatible MVC3 avec prise en charge de Html et Url.

L'exemple que j'ai écrit sur la page d'accueil du projet est purement un exemple d'utilisation d'un modèle de base personnalisé.

Vous pouvez en savoir plus sur la v3 sur https://github.com/Antaris/RazorEngine

19
Matthew Abbott

Vérifiez https://github.com/Antaris/RazorEngine/wiki/6.-Encoding-Values page. Je le copie/le colle ici:

Par défaut, RazorEngine est configuré pour coder en HTML. Cela présente parfois des problèmes lorsque certains caractères sont codés en HTML lorsque vous souhaitez que la sortie soit telle quelle.

Pour sortir quelque chose au format brut, utilisez la méthode intégrée @Raw () comme indiqué dans l'exemple suivant:

string template = "@Raw(Model.Data)";
var model = new { Data = "My raw double quotes appears here \"hello!\"" };

string result = Razor.Parse(template, model);

Ce qui devrait se traduire par:

My raw double quotes appears here "hello!"
30
Evereq

Cela fait plus d'un an, mais comme je n'ai trouvé aucune copie de travail nulle part sur Internet et que la page github est inactive, j'ai pensé partager mon implémentation pour ajouter la syntaxe d'assistance @Html à RazorEngine. Voici l'implémentation avec laquelle j'ai fini par utiliser l'implémentation d'Abu Haider comme point de départ.

Gracieuseté du commentaire de miketrash: Si vous essayez d'utiliser @ Html.Action (), vous devrez ajouter le RequestContext (vous pouvez utiliser HttpContext.Current.Request.RequestContext). Je n'ai pas inclus le contexte de la demande car il n'est pas toujours disponible pour mon application.

[RequireNamespaces("System.Web.Mvc.Html")]
public class HtmlTemplateBase<T>:TemplateBase<T>, IViewDataContainer
{
    private HtmlHelper<T> helper = null;
    private ViewDataDictionary viewdata = null;       

    public HtmlHelper<T> Html
    {
        get
        {
            if (helper == null) 
            {                  
                var writer = this.CurrentWriter; //TemplateBase.CurrentWriter
                var vcontext = new ViewContext() { Writer = writer, ViewData = this.ViewData};

                helper = new HtmlHelper<T>(vcontext, this);
            }
            return helper;
        }
    }

    public ViewDataDictionary ViewData
    {
        get
        {
            if (viewdata == null)
            {
                viewdata = new ViewDataDictionary();
                viewdata.TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = string.Empty };

                if (this.Model != null)
                {
                    viewdata.Model = Model;
                }
            }
            return viewdata;
        }
        set
        {
            viewdata = value;
        }
    }

    public override void WriteTo(TextWriter writer, object value)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");

        if (value == null) return;

        //try to cast to RazorEngine IEncodedString
        var encodedString = value as IEncodedString;
        if (encodedString != null)
        {
            writer.Write(encodedString);
        }
        else
        {
            //try to cast to IHtmlString (Could be returned by Mvc Html helper methods)
            var htmlString = value as IHtmlString;
            if (htmlString != null) writer.Write(htmlString.ToHtmlString());
            else
            {
                //default implementation is to convert to RazorEngine encoded string
                encodedString = TemplateService.EncodedStringFactory.CreateEncodedString(value);
                writer.Write(encodedString);
            }

        }
    }
}

J'ai également dû remplacer la méthode WriteTo de TemplateBase, car sinon RazorEngine encodera en html le résultat de la méthode d'assistance, ce qui signifie que vous échapperez aux '<', '>' et aux guillemets (voir cette question ). Le remplacement ajoute une vérification pour la valeur étant un IHtmlString avant de recourir à l'exécution d'un encodage.

12
mao47

C'est une question assez ancienne mais j'ai trouvé une bonne réponse sur coderwall . La solution consiste à utiliser:

@(new RawString("<strong>Bold!</strong>"))

ou juste:

@(new RawString(Model.YourHTMLStrinInModel))

J'espère que c'est utile.

12
Robert

Mes excuses, je n'ai pas la réputation requise de 50 pour ajouter un commentaire alors je dois mettre une réponse.

Si quelqu'un se demande (comme JamesStuddart l'était), la méthode SetTemplateBase () est manquante mais vous pouvez créer une instance de configuration pour initialiser un service avec votre modèle de base.

De http://razorengine.codeplex.com/discussions/285937 J'ai adapté mon code pour qu'il ressemble à:

var config = new RazorEngine.Configuration.TemplateServiceConfiguration
        {
            BaseTemplateType = typeof(MyHtmlTemplateBase<>)
        };

        using (var service = new RazorEngine.Templating.TemplateService(config))
        {
            // Use template service.
            Razor.SetTemplateService(service);
            result = Razor.Parse(templateString, model);
        }
5
VictorySaber

Ma réponse utilise la réponse de hannes neukermans .

J'avais besoin d'utiliser RazorEngine pour envoyer des e-mails incorporant des chaînes html stockées dans une base de données afin qu'elles puissent être modifiées par les utilisateurs administrateurs.

La configuration standard ne permettait pas à @ Html.Raw de fonctionner.

Dans ma classe d'emails, j'ai mis en place un nouveau Engine.Razor (Engine is static) qui incorpore les classes recommandées par Hannes. Je n'avais besoin que de la méthode Raw, mais vous pouvez évidemment en ajouter d'autres:

    public class HtmlSupportTemplateBase<T> : TemplateBase<T>
{
    public HtmlSupportTemplateBase()
    {
        Html = new MyHtmlHelper();
    }
    public MyHtmlHelper Html { get; set; }
}  

 public class MyHtmlHelper
{
    /// <summary>
    /// Instructs razor to render a string without applying html encoding.
    /// </summary>
    /// <param name="htmlString"></param>
    /// <returns></returns>
    public IEncodedString Raw(string htmlString)
    {
        return new RawString(WebUtility.HtmlEncode(htmlString));
    } 
}

Je pourrais ensuite utiliser @ Html.Raw dans mon modèle d'e-mails pour incorporer le html modifiable

public class Emails
{
    public static TemplateServiceConfiguration config 
                = new TemplateServiceConfiguration(); // create a new config

    public Emails()
    {   
        config.BaseTemplateType = typeof(HtmlSupportTemplateBase<>);// incorporate the Html helper class
        Engine.Razor = RazorEngineService.Create(config);// use that config to assign a new razor service
    }

    public static void SendHtmlEmail(string template,  EmailModel model)
    {           
        string emailBody 
             = Engine.Razor.RunCompile(template, model.Type.ToString(), typeof(EmailModel), model);

ce qui suit ne fait pas vraiment partie de la réponse mais donne du code utile à ceux qui l'utilisent pour les e-mails :)

        var smtpClient = getStaticSmtpObject(); // an external method not included here     
        MailMessage message = new MailMessage();
        message.From = new MailAddress(model.FromAddress);
        message.To.Add(model.EmailAddress); 
        message.Subject = model.Subject;
        message.IsBodyHtml = true;
        message.Body =  System.Net.WebUtility.HtmlDecode(emailBody);
        smtpClient.SendAsync(message, model); 
    }
}

Ensuite, je pourrais l'utiliser en transmettant la chaîne lue à partir du modèle .cshtml réel et du modèle contenant les données de courrier électronique. (ResolveConfigurationPath est une autre fonction externe que j'ai trouvée dans cette page )

string template = System.IO.File.ReadAllText(ResolveConfigurationPath("~/Views/Emails/MAPEmail.cshtml"));
SendHtmlEmail(template, model);
3
davaus

Html.Raw Solution la plus simple !! 3 étapes nécessaires

Étape 1: Hériter de TemplateBase:

public class HtmlSupportTemplateBase<T> : TemplateBase<T>
{
    public HtmlSupportTemplateBase()
    {
        Html = new MyHtmlHelper();
    }

    public MyHtmlHelper Html { get; set; }

}

Étape 2: Créez un objet qui rend disponible toutes les méthodes Html consommé par votre modèle. Dans cet exemple, Html.Raw et Html.Encode deviennent disponibles dans le cshtml. modèle

public class MyHtmlHelper
{
    /// <summary>
    /// Instructs razor to render a string without applying html encoding.
    /// </summary>
    /// <param name="htmlString"></param>
    /// <returns></returns>
    public IEncodedString Raw(string htmlString)
    {
        return new RawString(htmlString);
    }

    public string Encode(string value)
    {
        return System.Net.WebUtility.HtmlEncode(value);
    }

    public string Encode(object value)
    {
        return "do whatever";
    }
}

Étape 3:

var config = new TemplateServiceConfiguration
{
    TemplateManager = templateManager,
    BaseTemplateType = typeof(HtmlSupportTemplateBase<>)
};
2

Modification de la réponse mao 47 pour la dernière syntaxe de rasoir, cela prendra également en charge les vues partielles.

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Web.Hosting;
using System.Xml.Linq;
using RazorEngine.Configuration;
using RazorEngine.Templating;
public static class DynamicRazorTemplateParser
    {
        private static readonly IRazorEngineService service = RazorEngineService.Create(TemplateServiceConfiguration);
        public static string RunCompile<T>(string template, string placeholder, T model, DynamicViewBag viewBag) where T : class
        {
            var templateSource = new LoadedTemplateSource(template);
            return RunCompile(templateSource, placeholder, model, viewBag);
        }
        public static string RunCompile<T>(ITemplateSource template, string placeholder, T model, DynamicViewBag viewBag) where T : class 
        {            
                return service.RunCompile(template, placeholder, model.GetType(), model, viewBag);
        }
        public static string RunCompile(ITemplateSource template, string placeholder)
        {


                return service.RunCompile(template, placeholder);

        }

        private static TemplateServiceConfiguration TemplateServiceConfiguration
        {
            get
            {
                var config = new TemplateServiceConfiguration
                {
                    BaseTemplateType = typeof(HtmlTemplateBase<>),
                    TemplateManager = new TemplateManager()
                };
                //TODO: Is this the best way?
                var xDocument = XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "/Views/Web.config");
                if (xDocument.Root != null)
                {
                    var sysWeb = xDocument.Root.Element("system.web.webPages.razor");
                    if (sysWeb == null) return config;
                    var pages = sysWeb.Element("pages");
                    if (pages != null)
                    {
                        var namespaces = pages.Element("namespaces");
                        if (namespaces != null)
                        {
                            var namespacesAdd = namespaces.Elements("add")
                                .Where(x => x.Attribute("namespace") != null)
                                .Select(x =>

                                    x.Attribute("namespace").Value
                                );
                            foreach (var ns in namespacesAdd)
                            {
                                config.Namespaces.Add(ns);
                            }
                        }
                    }
                }
                return config;
            }
        }
        private class TemplateManager : ITemplateManager
        {
            private readonly ConcurrentDictionary<ITemplateKey, ITemplateSource> _dynamicTemplates = new ConcurrentDictionary<ITemplateKey, ITemplateSource>();
            private readonly string baseTemplatePath;
            public TemplateManager()
            {
                baseTemplatePath = HostingEnvironment.MapPath("~/Views/");
            }

            public ITemplateSource Resolve(ITemplateKey key)
            {
                ITemplateSource templateSource;
                if (this._dynamicTemplates.TryGetValue(key, out templateSource))
                    return templateSource;

                string template = key.Name;
                var ubuilder = new UriBuilder();
                ubuilder.Path = template;
                var newURL = ubuilder.Uri.LocalPath.TrimStart('/');
                string path = Path.Combine(baseTemplatePath, string.Format("{0}", newURL));


                string content = File.ReadAllText(path);
                return new LoadedTemplateSource(content, path);
            }

            public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
            {
                return new NameOnlyTemplateKey(name, resolveType, context);
            }

            public void AddDynamic(ITemplateKey key, ITemplateSource source)
            {
                this._dynamicTemplates.AddOrUpdate(key, source, (k, oldSource) =>
                {
                    if (oldSource.Template != source.Template)
                        throw new InvalidOperationException("The same key was already used for another template!");
                    return source;
                });
            }
        }
    }


using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using RazorEngine.Templating;
using RazorEngine.Text;
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global
// ReSharper disable MemberCanBePrivate.Global

namespace Common.Core.Razor
{
    [RequireNamespaces("System.Web.Mvc.Html")]
    public class HtmlTemplateBase<T> : RazorEngine.Templating.HtmlTemplateBase<T>, IViewDataContainer
    {
        private HtmlHelper<T> helper;
        private ViewDataDictionary viewdata;
        private TempDataDictionary tempdata;
        private AjaxHelper<T> ajaxHelper;
        private ViewContext viewContext;
        private UrlHelper urlHelper;
        private readonly RequestContext _requestContext = HttpContext.Current.Request.RequestContext;


        public UrlHelper Url => urlHelper ?? (urlHelper = new UrlHelper(_requestContext));

        public ViewContext ViewContext
        {
            get
            {
                if (viewContext != null) return viewContext;
                viewContext = GetViewContext();
                return viewContext;
            }
        }

        public AjaxHelper<T> Ajax
        {
            get
            {
                if (ajaxHelper != null) return ajaxHelper;
                ajaxHelper = new AjaxHelper<T>(ViewContext, this);
                return ajaxHelper;
            }
        }

        public HtmlHelper<T> Html
        {
            get
            {
                if (helper != null) return helper;
                helper = new HtmlHelper<T>(ViewContext, this);
                return helper;
            }
        }

        public ViewDataDictionary ViewData
        {
            get
            {
                if (viewdata == null)
                {
                    viewdata = new ViewDataDictionary
                    {
                        TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = string.Empty }
                    };

                    if (Model != null)
                    {
                        viewdata.Model = Model;
                    }
                }
                return viewdata;
            }
            set
            {
                viewdata = value;
            }
        }
        public TempDataDictionary TempData
        {
            get { return tempdata ?? (tempdata = new TempDataDictionary()); }
            set
            {
                tempdata = value;
            }
        }
        public virtual string RenderView()
        {
            using (var writer = new StringWriter())
            {
                ViewContext.View.Render(ViewContext, CurrentWriter);
                return writer.GetStringBuilder().ToString();
            }
        }


        private ViewContext GetViewContext()
        {
            if (HttpContext.Current == null) throw new NotImplementedException();
            var requestContext = _requestContext;
            var controllerContext = ControllerContext(requestContext);

            var view = GetView(requestContext, controllerContext);
            //Can't check if string writer is closed, need to catch exception
            try
            {
                var vContext = new ViewContext(controllerContext, view, ViewData, TempData, CurrentWriter);
                return vContext;

            }
            catch
            {
                using (var sw = new StringWriter())
                {
                    var vContext = new ViewContext(controllerContext, view, ViewData, TempData, sw);
                    return vContext;
                }
            }
        }

        private IView GetView(RequestContext requestContext, ControllerContext controllerContext)
        {
            if ((string)requestContext.RouteData.DataTokens["Action"] != null)
            {
                requestContext.RouteData.Values["action"] = (string)requestContext.RouteData.DataTokens["Action"];
            }

            var action = requestContext.RouteData.GetRequiredString("action");
            var viewEngineResult = ViewEngines.Engines.FindPartialView(controllerContext, action);
            if (viewEngineResult != null && viewEngineResult.View != null)
            {
                return viewEngineResult.View;
            }

            viewEngineResult = ViewEngines.Engines.FindView(controllerContext, action, null);
            if (viewEngineResult == null)
            {
                throw new Exception("No PartialView assigned in route");
            }
            return viewEngineResult.View;


        }

        public void SetView(string view)
        {
            _requestContext.RouteData.DataTokens["Action"] = view;
        }


        private ControllerContext ControllerContext(RequestContext requestContext)
        {
            ControllerBase controllerBase;
            var routeDataValue = "EmptyController";
            if (requestContext.RouteData.Values["controller"] != null && (string)requestContext.RouteData.Values["controller"] != routeDataValue)
            {
                var controllerName = (string)requestContext.RouteData.Values["controller"];
                IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext, controllerName);
                controllerBase = controller as ControllerBase;
            }
            else
            {

                var controller = new EmptyController();
                controllerBase = controller; //ControllerBase implements IController which this returns
                requestContext.RouteData.Values["controller"] = routeDataValue;
            }
            var controllerContext =
                new ControllerContext(requestContext.HttpContext, requestContext.RouteData, controllerBase);
            return controllerContext;
        }
        private class EmptyController : Controller { }
        public override void WriteTo(TextWriter writer, object value)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            if (value == null) return;

            //try to cast to RazorEngine IEncodedString
            var encodedString = value as IEncodedString;
            if (encodedString != null)
            {
                writer.Write(encodedString);
            }
            else
            {
                //try to cast to IHtmlString (Could be returned by Mvc Html helper methods)
                var htmlString = value as IHtmlString;
                if (htmlString != null) writer.Write(htmlString.ToHtmlString());
                else
                {
                    //default implementation is to convert to RazorEngine encoded string
                    base.WriteTo(writer, value);

                }

            }
        }
    }
}
0
jonmeyer