Search code examples
c#razorrazorengine

RazorEngine @Html helpers work, but escape the Html


I am using RazorEngine to parse templates from html snippets on a web page. (This is a legacy system that switching to Mvc Razor views isn't possible, so we are switching small sections over to using RazorEngine where it makes sense). There are many of questions on SO and the internet trying to get Mvc's Html and Url helpers to work with Razor engine. To get @Html syntax to work, I've modified some code found here to add Html to the base template:

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

After a lot of debugging into the Html source code I think I've managed to instantiate everything that the Html helper needs, and it runs successfully for @Html.Label... The problem is that the resulting html is:

&lt;label for=&quot;MyNumber&quot;&gt;MyNumber&lt;/label&gt;

When it obviously should be:

<label for="MyNumber">MyNumber</label>

I am stumped as to how to fix this. I was not able to find how the encoding happens when looking through the RazorEngine source. My initial thought was that the TextWriter must be encoding the value but I have not been able to confirm this. How can I get @Html.BlahFor() to render un-escaped html?


Solution

  • I have found a workaround for the problem I was facing. RazorEngine's base Template will automatically encode a string (or object) if it doesn't cast to an IEncodedString. In my case, I solved this issue by overriding the WriteTo method in my template class:

    public override void WriteTo(TextWriter writer, object value)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");
    
        if (value == null) return;
    
        var encodedString = value as IEncodedString;
        if (encodedString != null)
        {
            writer.Write(encodedString);
        }
        else
        {
            var htmlString = value as IHtmlString;
            if(htmlString != null) 
                writer.Write(htmlString.ToHtmlString());
            else
            {
                //This was the base template's implementation:
                encodedString = TemplateService.EncodedStringFactory.CreateEncodedString(value);
                writer.Write(encodedString);
            }
        }
    }
    

    I have yet to see if this change will cause any errors, but now that I found the method it should be relatively easy to change in the future.

    EDIT: Checked to see Html helpers return an MvcHtmlString, which implements IHtmlString, so by adding a cast to this interface, we can avoid encoding the html returned by the helpers, while still having a safety in place for any other callers of this method.