英文:
Golang error handling mistake
问题
以下程序是官方教程中的Web服务器的自定义版本,位于此页面。我只修改了save()和loadPage()函数,以便使用jmcvetta的neoism包将页面保存到neo4j中。
// 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)
}
我的问题是,在loadPage()函数中,当返回的页面没有内容时,似乎没有返回任何错误,导致处理程序尝试使用空结构执行模板(至少我认为是这样)。
有人看到我哪里做错了吗?
编辑:当我尝试加载一个不存在的页面时,这是输出:
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
说明:对于已经存在的页面,程序按预期工作,我能够编辑页面并稍后加载它们。
英文:
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.
答案1
得分: 3
我怀疑在不运行代码的情况下,错误可能出现在这里:
db.Cypher(&cq)
r := res[0]
if r.Title == "" || r.Body == "" {
return nil, errors.New("页面不存在")
}
代码假设存在一个 res[0] 的值。你应该检查一下:
if len(res) == 0 {
return nil, errors.New("页面不存在")
}
你的错误出现在 loadPage 函数中,是一个索引错误。
英文:
Without running the code I suspect the error is here:
db.Cypher(&cq)
r := res[0]
if r.Title == "" || r.Body == "" {
return nil, errors.New("Page doesn't exist")
}
the code assumes that there is a res[0] value. You should check
if len(res) == 0 {
return nil, errors.New("Page does not exists")
}
You error is in your code in loadPage, and is an index error.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论