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.
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),
})