3

I'm following this tutorial: golang tutorial - wiki and I've managed to get everything working except the last point in the "other task" section. My Implementation of the tutorial:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "html/template"
    "regexp"
)

type Page struct {
    Title string
    Body []byte
}

func (p *Page) save() error {
    filename := "data/" + p.Title + ".txt"
    return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
    filename := "data/" + title + ".txt"
    body, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    return &Page{Title: title, Body: body}, nil
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
    t, _ := template.ParseFiles("template/" + tmpl + ".html");
    t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/view/"):]
    p, err := loadPage(title)
    if err != nil {
        http.Redirect(w, r, "/edit/" + title, http.StatusFound);
        return
    }
    expr := regexp.MustCompile(`\[.+\]`)
    p.Body = expr.ReplaceAllFunc(p.Body, func ( match []byte) []byte {
        return []byte("<a href='/view/" + string(match) + "'>" + string(match) + "</a>")
    })
    renderTemplate(w, "view", p);
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/save/"):]
    body := r.FormValue("body")
    p := &Page{Title: title, Body: []byte(body)}
    p.save()
    http.Redirect(w, r, "/view/" + title, http.StatusFound)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/edit/"):]
    p, err := loadPage(title);

    if err != nil {
        p = &Page{Title: title}
    }
    renderTemplate(w, "edit", p);
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.Redirect(w, r, "/view/index", http.StatusFound)    
        })
    http.HandleFunc("/view/", viewHandler)
    http.HandleFunc("/edit/", editHandler)
    http.HandleFunc("/save/", saveHandler)
    http.ListenAndServe(":8080", nil)
    fmt.Println("Running");
}

html/template engine prints the anchor tag as expected, but escapes it with html entities. I've not been able to find appropriate way of doing it.

1 Answer 1

6

Use template.HTML to mark the body as safe HTML.

The following function converts the body to HTML.

// Move to package-level variable so that it's compile once.
var linkPat = regexp.MustCompile(`\[.+\]`)

func toHTML(s string) template.HTML {

  // Escape everything in the string first to ensure that
  // special characters ('<' for example) are displayed as
  // characters and not treated as markup.
  s = template.HTMLEscapeString(s)

  // Insert the links.
  s = linkPat.ReplaceAllStringFunc(s, func(m string) string {
    s = s[1 : len(s)-1]
    return "<a href='/view/" + m + "'>" + m + "</a>"
  })

  return template.HTML(s)
}

Render the page using:

renderTemplate(w, "edit", map[string]interface{}{
   "Title": p.Body,
   "Body": toHTML(p.Body),
})
Sign up to request clarification or add additional context in comments.

1 Comment

A tiny bit too quick for me .. and wrapped in a nicer function. +1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.