2016-05-31 7 views
0

私はこのようなコードを持っている:DB接続にハンドラからアクセスできないのはなぜですか?

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:[email protected](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にアクセスしてみてください。私はグローバル変数であることを中に設定され、私はbooksIndexからdbにアクセスできると思っていた

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 

:このような

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

。 db初期化をbooksIndexに移動すると、エラーは表示されません。

コードのどの部分が間違っていますか?

また、私はゴランで新しくなっています。コードの整理方法についてご意見がありましたら、教えてください。ありがとう.. :)

答えて

1

関数内のdb変数は、シャドーグローバル変数です。 これを行うと:

db,err:= 

それは新しいローカル変数デシベルに割り当てます。 これは同じブロックではないためです。標準によれば、通常の変数宣言とは異なり

、変数を再宣言することができる短い変数の宣言は、彼らが最初に宣言された提供先に 同じブロック(またはブロックが機能 体である場合、パラメータリスト)とで同じタイプであり、少なくとも1つの非ブランク変数 は新しいものです。その結果、再宣言は、 複数変数の短い宣言にしか現れません。再宣言では、新しい変数 が導入されません。オリジナルに新しい値を割り当てます。

したがって、グローバル変数はnilポインタのままです。そして、それがアクセスされているとき、あなたは無限ポインタ逆参照を得る。

これをdに変更し、後でdbに割り当てる。それともこれは(rahmatでコメントで与えられる)より正確である:=

d , err = 

私は、ハンドラ、モデルなどのために別々のファイルを持っており、コードを整理する方法にhttps://github.com/mattermost/platformに見て、あなたをお勧めします。

+0

Ahh ..ありがとう!それを 'db、err = sql.Open(" mysql "、DefaultDSN)'に変更しました。また、動作します。:) – rahmat

+0

はい。私はあなたもエラーを宣言していることに気付かなかった。 – khrm

+0

人々が主に答えを読んでいるので、回答としてあなたのコメントを追加しました。それが助けてくれたら、アップアップしてください。 – khrm

関連する問題