为什么无法从处理程序访问我的数据库连接?

时间:2016-05-31 04:59:11

标签: database go

我有这样的代码:

package main

import (
    "database/sql"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"

    _ "github.com/go-sql-driver/mysql"
)

// Default constant for configuration
const DefaultHTTPAddr = ":8080"
const DefaultDSN = "root:root@tcp(127.0.0.1:3306)/librarian"

// Parameters
var (
    httpAddr string
    dsn      string
    db       *sql.DB
)

// init initializes this package.
func init() {
    flag.StringVar(&httpAddr, "addr", DefaultHTTPAddr, "Set the HTTP bind address")
    flag.StringVar(&dsn, "dsn", DefaultDSN, "Set the Data Source Name")
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
        flag.PrintDefaults()
    }

}

type Book struct {
    id     int
    title  string
    author string
}

func main() {
    flag.Parse()

    var err error
    db, err := sql.Open("mysql", DefaultDSN)
    if err != nil {
        log.Fatal(err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal(err)
    }

    // handler
    http.HandleFunc("/", homepage)
    http.HandleFunc("/books", booksIndex)

    log.Println("httpd started successfully")
    http.ListenAndServe(httpAddr, nil)
}

func booksIndex(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), 405)
        return
    }

    rows, err := db.Query("SELECT * FROM books")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    books := make([]*Book, 0)
    for rows.Next() {
        bk := new(Book)
        err = rows.Scan(&bk.id, &bk.title, &bk.author)
        if err != nil {
            log.Fatal(err)
        }
        books = append(books, bk)
    }
    if err = rows.Err(); err != nil {
        log.Fatal(err)
    }

    for _, bk := range books {
        fmt.Fprintf(w, "%v, %v, %v\n", bk.id, bk.title, bk.author)
    }
}

func homepage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome!")
}

每当我尝试访问/books时,它总是恐慌。

$ curl localhost:8080/books
curl: (52) Empty reply from server

喜欢这个:

2016/05/31 11:56:38 http: panic serving 127.0.0.1:51711: runtime error: invalid memory address or nil pointer dereference
goroutine 6 [running]:
net/http.(*conn).serve.func1(0xc820074100)
        /usr/local/opt/go/libexec/src/net/http/server.go:1389 +0xc1
panic(0x357b40, 0xc82000a150)
        /usr/local/opt/go/libexec/src/runtime/panic.go:443 +0x4e9
database/sql.(*DB).conn(0x0, 0xc820010b01, 0xc8200de000, 0x0, 0x0)
        /usr/local/opt/go/libexec/src/database/sql/sql.go:778 +0xac9
database/sql.(*DB).query(0x0, 0x4022c0, 0x13, 0x0, 0x0, 0x0, 0x3c4601, 0x6, 0x0, 0x0)
        /usr/local/opt/go/libexec/src/database/sql/sql.go:1073 +0x46
database/sql.(*DB).Query(0x0, 0x4022c0, 0x13, 0x0, 0x0, 0x0, 0xc820016280, 0x0, 0x0)
        /usr/local/opt/go/libexec/src/database/sql/sql.go:1061 +0xa3
main.booksIndex(0x6979c0, 0xc82006da00, 0xc8200e2000)
        /Users/rahmatawaludin/gocode/src/github.com/rahmatawaludin/librarian/main.go:68 +0xd9
net/http.HandlerFunc.ServeHTTP(0x4666f8, 0x6979c0, 0xc82006da00, 0xc8200e2000)
        /usr/local/opt/go/libexec/src/net/http/server.go:1618 +0x3a
net/http.(*ServeMux).ServeHTTP(0xc820010ba0, 0x6979c0, 0xc82006da00, 0xc8200e2000)
        /usr/local/opt/go/libexec/src/net/http/server.go:1910 +0x17d
net/http.serverHandler.ServeHTTP(0xc820074080, 0x6979c0, 0xc82006da00, 0xc8200e2000)
        /usr/local/opt/go/libexec/src/net/http/server.go:2081 +0x19e
net/http.(*conn).serve(0xc820074100)
        /usr/local/opt/go/libexec/src/net/http/server.go:1472 +0xf2e
created by net/http.(*Server).Serve
        /usr/local/opt/go/libexec/src/net/http/server.go:2137 +0x44e

我认为我可以从db访问booksIndex,因为我设置为全局变量。当我将db初始化移动到booksIndex时,错误不会显示出来。

我的代码中哪些部分有问题?

另外,我是Golang的新手。如果您对如何组织我的代码有任何建议,请告诉我。谢谢.. :))

1 个答案:

答案 0 :(得分:1)

你的db变量函数是阴影全局变量。 当你这样做时:

db,err:=

它将它分配给新的本地变量db。 这是因为它不是来自同一块。根据标准:

  

与常规变量声明不同,短变量声明可以重新声明变量,前提是它们最初是先声明的   在同一个块中(如果块是函数,则参数列出)   body)具有相同的类型,以及至少一个非空白变量   是新的。因此,重新声明只能出现在   多变量简短声明。重新申报不会引入   新变量;它只是为原始版本赋予了一个新值。

所以全局变量仍然是nil指针。当它是访问时,你得到nil指针解除引用

将其更改为d,然后将其分配给db。或者哪个更正确(如rahmat的评论中所述):=

d , err = 

我建议您为处理程序,模型等提供单独的文件。并查看https://github.com/mattermost/platform如何组织代码。