The following program is a custom version of the webserver from the official tutorial at this page. Only I've modified the save() and loadPage() functions to save my pages to neo4j using jmcvetta's package neoism.
// web1 project main.go
package main
import (
"errors"
"fmt"
"github.com/jmcvetta/neoism"
"html/template"
"io/ioutil"
"net/http"
"regexp"
)
type Page struct {
Title string
Body string
}
func (p *Page) save() error {
db, err := neoism.Connect("http://localhost:7474/db/data")
if err != nil {
return err
}
res := []struct {
N neoism.Node
}{}
cq := neoism.CypherQuery{
Statement: "MERGE (n:Page {title: {title}}) ON MATCH SET n.body = {body} RETURN n",
Parameters: neoism.Props{"title": p.Title, "body": p.Body},
Result: &res,
}
db.Cypher(&cq)
return nil
}
func loadPage(title string) (*Page, error) {
db, err := neoism.Connect("http://localhost:7474/db/data")
if err != nil {
return nil, err
}
res := []struct {
Title string `json:"a.title"` // `json` tag matches column name in query
Body string `json:"a.body"`
}{}
cq := neoism.CypherQuery{
Statement: `
MATCH (a:Page)
WHERE a.title = {name}
RETURN a.title, a.body
`,
Parameters: neoism.Props{"name": title},
Result: &res,
}
db.Cypher(&cq)
r := res[0]
if r.Title == "" || r.Body == "" {
return nil, errors.New("Page doesn't exist")
}
return &Page{Title: r.Title, Body: r.Body}, nil
}
func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: body}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadFile("home.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "%s", body)
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/home", http.StatusFound)
}
var templates = template.Must(template.ParseFiles("edit.html", "view.html"))
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates.ExecuteTemplate(w, tmpl+".html", p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2])
}
}
func main() {
http.HandleFunc("/view/", makeHandler(viewHandler))
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))
http.HandleFunc("/home", homeHandler)
http.HandleFunc("/", rootHandler)
http.ListenAndServe(":8082", nil)
}
My problem is, i believe, in the the loadPage() function, which doesn't seem to return any error when the page returned has an empty body, causing the handlers to try executing templates with empty structs (at least i tend to think that's what it is). Does any one see where i did wrong ?
Edit : This is the output when i try to load a page that doesn't already exist :
2014/06/19 23:13:35 http: panic serving 127.0.0.1:60326: runtime error: index out of range
goroutine 5 [running]:
net/http.func·009()
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1093 +0xb1
runtime.panic(0x6d1f20, 0xa977d7)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/runtime/panic.c:248 +0x11b
main.loadPage(0xc08407a12a, 0x6, 0x332978, 0xaa7560, 0x3)
C:/Users/Nicolas/Go/src/web1/main.go:55 +0x369
main.editHandler(0x332940, 0xc08400b140, 0xc084019750, 0xc08407a12a, 0x6)
C:/Users/Nicolas/Go/src/web1/main.go:72 +0x34
main.func·001(0x332940, 0xc08400b140, 0xc084019750)
C:/Users/Nicolas/Go/src/web1/main.go:121 +0xc9
net/http.HandlerFunc.ServeHTTP(0xc084024ae0, 0x332940, 0xc08400b140, 0xc084019750)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1220 +0x43
net/http.(*ServeMux).ServeHTTP(0xc0840381b0, 0x332940, 0xc08400b140, 0xc084019750)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1496 +0x166
net/http.serverHandler.ServeHTTP(0xc084005e10, 0x332940, 0xc08400b140, 0xc084019750)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1597 +0x171
net/http.(*conn).serve(0xc08403f580)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1167 +0x7ba
created by net/http.(*Server).Serve
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease250988475/go/src/pkg/net/http/server.go:1644 +0x28e
Precision : for pages that already exist, the program works as intended, I'm able to edit pages and load them later.