RazorEngine问题与@Html

RazorEngine issues with @Html

我正在使用RazorEngine渲染一些基本内容(一个非常粗糙的内容管理系统)。

直到我将任何@Html语法包含在标记中之前,它的效果都很好。

如果标记包含@html,则会出现以下错误:

Unable to compile template. The name 'Html' does not exist in the
current context

这是呈现标记的视图:

1
2
3
4
5
6
7
8
@Model Models.ContentPage

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

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

我已经在RazorEngine的Codeplex站点上看到@Html的使用(我知道那里的版本已经过时,并且我是通过nuget获得版本的)。

任何帮助都会很棒。


检查https://github.com/Antaris/RazorEngine/wiki/6.-Encoding-Values页面。我在这里复制/粘贴它:

默认情况下,RazorEngine配置为编码为HTML。当您希望按原样输出时,有时会出现某些字符编码为HTML的问题。

要以原始格式输出内容,请使用@Raw()内置方法,如以下示例所示:

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

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

这应导致:

1
My raw double quotes appears here"hello!"


HtmlUrl助手属性是MVC在其视图引擎中实现Razor的实际功能。如果不专门定制自定义基础模板,则当前不支持HtmlUrl

即将发布的v3版本将与相关的RazorEngine.Web版本一起发布,该版本将有望包含具有HtmlUrl支持的MVC3兼容基本模板。

我在项目主页上编写的示例纯粹是使用自定义基本模板的示例。

您可以在https://github.com/Antaris/RazorEngine上找到有关v3的更多信息。


这是一个很老的问题,但是我在coderwall上找到了很好的答案。解决方案是使用:

1
@(new RawString("Bold!"))

要不就:

1
@(new RawString(Model.YourHTMLStrinInModel))

希望对您有所帮助。


这已经一年多了,但是由于我没有在互联网上的任何地方找到工作副本,并且github页面处于非活动状态,所以我想我会分享我的实现,以向RazorEngine添加@Html帮助器语法。这是我最后得到的实施,以Abu Haider的实施为起点。

感谢miketrash的评论:如果尝试使用@ Html.Action(),则需要添加RequestContext(可以使用HttpContext.Current.Request.RequestContext)。我没有包括请求上下文,因为它并不总是可用于我的应用程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[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);
            }

        }
    }
}

我还必须重写TemplateBaseWriteTo方法,因为否则RazorEngine将对辅助方法的结果进行html编码,这意味着您将转义'<','>'和引号(请参阅此问题)。覆盖在执行编码之前添加了对值为IHtmlString的检查。


抱歉,我没有50条信誉才能添加评论,因此必须提出答案。

如果有人想知道(就像JamesStuddart一样),则缺少SetTemplateBase()方法,但是您可以创建一个配置实例以使用基本模板初始化服务。

来自http://razorengine.codeplex.com/discussions/285937,我修改了代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
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);
        }

我的答案使用了hannes neukermans的答案。

我需要使用RazorEngine发送包含存储在数据库中的html字符串的电子邮件,以便管理员用户可以对其进行编辑。

标准配置不允许@ Html.Raw工作。

在我的电子邮件课程中,我设置了一个新的Engine.Razor(Engine是静态的),其中包含了Hannes建议的课程。我只需要Raw方法,但是您显然可以添加其他方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    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));
    }
}

然后,我可以在电子邮件模板中使用@ Html.Raw来合并可编辑的html。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);

以下内容并不是答案的真正组成部分,但为使用它发送电子邮件的人提供了有用的代码:)

1
2
3
4
5
6
7
8
9
10
        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);
    }
}

然后,我可以通过传入从实际的.cshtml模板读取的字符串和包含电子邮件数据的模型来使用它。 (ResolveConfigurationPath是我在此页面中找到的另一个外部函数)

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

HTML.Raw最简单的解决方案!需要3个步骤

步骤1:从TemplateBase继承:

1
2
3
4
5
6
7
8
9
10
public class HtmlSupportTemplateBase< T > : TemplateBase< T >
{
    public HtmlSupportTemplateBase()
    {
        Html = new MyHtmlHelper();
    }

    public MyHtmlHelper Html { get; set; }

}

步骤2:创建一个对象,使模板使用的所有Html方法可用。
在此示例中,Html.Raw和Html.Encode在cshtml中可用。模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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";
    }
}

第三步:

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


修改mao47答案以获取最新的剃刀语法,这也将支持部分视图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
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);

                }

            }
        }
    }
}