Golang错误处理错误

huangapple go评论95阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2014年6月20日 04:54:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/24316030.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定