42

I have followed this tutorial: http://golang.org/doc/articles/wiki/final.go and have slightly modified it for my needs/wants. The problem is I would like to support HTML in the templates. I realize this is a security risk but it's not a concern at the moment.

The result of a page render:

<h1>this<strong>is</strong>a test</h1>

Let me explain a little bit of the code:

type Page struct {
    Title string
    Body  []byte
}

The data I would like to have HTML is stored in Page.Body. This is type []byte which means I can't (or can I?) run html/template.HTML(Page.Body) as that function expects a string.

I have this which pre-renders the templates:

var (
    templates = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html"))
)

And the actual ExecuteTemplate looks like this:

err := templates.ExecuteTemplate(w, tmpl+".html", p)

Where w is w http.ResponseWriter, tmpl is tmpl string, and p is p *Page

Finally my 'view.html' (template) looks like the following:

<h1>{{.Title}}</h1>
<p>[<a href="/edit/{{.Title}}">edit</a>]</p>
<div>{{printf "%s" .Body}}</div>

Things I have tried:

  • {{printf "%s" .Body | html}} doesn't do anything
  • I have included github.com/russross/blackfriday (Markdown processor) and have run p.Body = blackfriday.MarkdownCommon(p.Body) which correctly converts Markdown to HTML, but the HTML is still output as entities.
  • EDIT: I have attempted the following bit of code (I don't know why the format is messed up) and it still outputs the exact same.

    var s template.HTML s = template.HTML(p.Body) p.Body = []byte(s)

Any guidance is greatly appreciated. If I'm being confusing please ask and I can modify my question.

Peter
  • 3,144
  • 11
  • 37
  • 56

8 Answers8

78

Convert your []byte or string to type template.HTML (documented here)

p.Body = template.HTML(s) // where s is a string or []byte

Then, in your template, just:

{{.Body}}

It will be printed without escaping.

EDIT

In order to be able to include HTML in you page's body you need to change the Page type declaration:

type Page struct {
    Title string
    Body  template.HTML
}

then assign to it.

thwd
  • 23,956
  • 8
  • 74
  • 108
  • That doesn't work. I receive the following message: `cannot use "html/template".HTML(p.Body) (type "html/template".HTML) as type []byte in assignment` – Peter Aug 12 '13 at 20:59
  • 11
    don't just copy-paste and say it doesn't work. you obviously need to change the type of your Body field to be able to assign something of type template.HTML to it. – thwd Aug 12 '13 at 21:38
  • Please see the Edit I made last night to my question right after I posted the comment. I did make a change to the type of Body and I'm still seeing the exact same results. – Peter Aug 13 '13 at 16:07
  • I added a function called 'noescape' that converts a string to `template.HTML`, then `printf` to it in a pipeline. Works magic. – Matt Joiner Sep 19 '15 at 04:59
  • I spent a couple of days getting very angry at my template for working when I passed strings, but not when I passed html strings... Thanks! – T1960CT Apr 30 '19 at 00:14
22

Take a look at the template.HTML type. It can be used to encapsulate a known safe fragment of HTML (like the output from Markdown). The "html/template" package will not escape this this type.

type Page struct {
    Title string
    Body template.HTML
}

page := &Page{
    Title: "Example",
    Body:  template.HTML(blackfriday.MarkdownCommon([]byte("foo bar")),
}

I usually write my own func Markdown(text string) html.Template method that calls blackfriday with the appropriate config and does some type conversions. Another alternative might be also to register a "html" func in the template parser, that allows you to output any value without any escaping by doing something like {{html .MySafeStr}}. The code might look like:

var tmpl = template.Must(template.New("").Funcs(template.FuncMap{
    "html": func(value interface{}) template.HTML {
        return template.HTML(fmt.Sprint(value))
    },
}).ParseFiles("file1.html", "file2.html"))
tux21b
  • 90,183
  • 16
  • 117
  • 101
20

I created a custom function for the template as follows:

func noescape(str string) template.HTML {
    return template.HTML(str)
}

var fn = template.FuncMap{
    "noescape": noescape,
}

Then on your template:

{{ noescape $x.Body }}
majidarif
  • 18,694
  • 16
  • 88
  • 133
3

Here's an approach that doesn't require any changes to your existing structs, and a very minimal, additive change to your templates:

Change these lines:

var (
    templates = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html"))
)

to this (include a funcmap with a function that will output un-escaped HTML):

var templates = template.Must(template.New("main").Funcs(template.FuncMap{
    "safeHTML": func(b []byte) template.HTML {
        return template.HTML(b)
    },
}).ParseFiles("tmpl/edit.html", "tmpl/view.html"))

And then just change your template HTML from this:

<div>{{printf "%s" .Body}}</div>

to this (use your new function):

<div>{{ .Body | safeHTML }}</div>

Much easier!

Astockwell
  • 1,498
  • 13
  • 11
1

I'm using Beego and React.js and fought for hours trying to get the JSX parser to run. Turns out html/template strips out comments especially the js doc block /** @jsx React.DOM */.

Got around it by creating a special method to Type the comment as JS and calling it from within the template.

// Create a method in your controller (I'm using Beego)
func jsxdoc()(out template.JS) {
    return template.JS(`/** @jsx React.DOM */`)
}

// Add method to your function map available to views
beego.AddFuncMap("jsxdoc", jsxdoc)

// In template
<script type="text/jsx">
    {{ jsxdoc }}
    var CommentBox = React.createClass({
      render: function() {
        return (
          <div class="commentBox">
            Hello, world! I am a CommentBox.
          </div>
        );
      }
    });
    React.renderComponent(
      <CommentBox />,
      document.getElementById('content')
    );
</script>
mangoavenger
  • 117
  • 2
0

For clarification and a much simpler way of passing HTML to template, see

https://groups.google.com/forum/#!topic/golang-nuts/8L4eDkr5Q84

Just create your HTML string via go and pass it into your template, e.g.:

Sout := ""
.
.

    Sout += fmt.Sprintf(`<tr><td>%s<td align=center>%.2f<td>%s<td>%s<td>%s<td>%s<td align=center>%d<td align=center>%d
                    <td align=center>%d`, AccountID, amount, remissiondetails, created, begins, ends,
                    freePDFs, freeinformants, freeSDQs)

.
.
    render(w, "templates/Waivers.html", map[string]interface{}{ "Body":template.HTML(Sout), })
user2099484
  • 4,417
  • 2
  • 21
  • 9
0

In my case (where I am populating a view struct with a list of Activity), I had to change the property Message string to Message template.HTML. Then, when setting the property value I can call activity.Message = template.HTML("The <b>HTML</b>").

crmepham
  • 4,676
  • 19
  • 80
  • 155
-1

Why not convert the []byte to a string? You can do it like this:

str := string(page.Body)
David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • I tried that, the issue is `template.ExecuteTemplate` needs a pointer to Page which requires it to be a `[]byte` I tried doing exactly what you mentioned and received the following error: `cannot use string(p.Body) (type string) as type []byte in assignment` . Likewise changing my struct to use a string instead of a []byte caused other issues when saving the content to file. – Peter Aug 11 '13 at 19:47