GET /articles/:article_id Not working Gin-Gonic/Gin

1.6k views Asked by At

I am working on an API and GET and POST are working fine except when I try and get a select record by its ID (e.g. /articles/2). The article exists and when retrieving all records via the /articles route I get a proper response. Here is the stack trace.

    $ go run main.go
    [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
    - using env:   export GIN_MODE=release
    - using code:  gin.SetMode(gin.ReleaseMode)

    [GIN-debug] GET   /                         --> main.index (3 handlers)
    [GIN-debug] GET   /articles                 --> main.ArticlesList (3 handlers)
    [GIN-debug] POST  /articles                 --> main.ArticlePost (3 handlers)
    [GIN-debug] GET   /articles/:article_id     --> main.ArticlesDetail (3 handlers)
    [GIN-debug] Listening and serving HTTP on :8000
    2015/06/18 10:31:49 Panic recovery -> interface conversion: error is *errors.errorString, not *errors.Error
    c:/go/src/runtime/panic.go:387 (0x4114b6)
            gopanic: reflectcall(unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
    c:/go/src/runtime/iface.go:181 (0x40ae1a)
            assertI2T: panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
    c:/Users/Fresh/go/src/github.com/jisaw/portfolio-server/main.go:37 (0x401261)
            checkErr: log.Fatalln(msg, err.(*errors.Error).ErrorStack())
    c:/Users/Fresh/go/src/github.com/jisaw/portfolio-server/main.go:97 (0x401e2c)
            getArticle: checkErr(err, "selectOne failed")
    c:/Users/Fresh/go/src/github.com/jisaw/portfolio-server/main.go:60 (0x4016a7)
            ArticlesDetail: article := getArticle(a_id)
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/context.go:86 (0x43694a)
            (*Context).Next: c.handlers[c.index](c)
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/logger.go:56 (0x442ff0)
            func.007: c.Next()
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/context.go:86 (0x43694a)
            (*Context).Next: c.handlers[c.index](c)
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/recovery.go:43 (0x4437e0)
            func.009: c.Next()
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/context.go:86 (0x43694a)
            (*Context).Next: c.handlers[c.index](c)
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/gin.go:249 (0x43b275)
            (*Engine).handleHTTPRequest: context.Next()
    c:/Users/Fresh/go/src/github.com/gin-gonic/gin/gin.go:230 (0x43aff9)
            (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
    c:/go/src/net/http/server.go:1703 (0x4ad385)
            serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
    c:/go/src/net/http/server.go:1204 (0x4ab378)
            (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
    c:/go/src/runtime/asm_386.s:2287 (0x435c01)
            goexit:

And the Code

package main

import (
    "github.com/gin-gonic/gin"
    "database/sql"
    "github.com/coopernurse/gorp"
    _ "github.com/mattn/go-sqlite3"
    "log"
    "time"
    "strconv"
    "github.com/go-errors/errors"
)

type Article struct {
    Id int64 `db:"article_id"`
    Created int64
    Title string
    Content string
}

var dbmap = initDb()

func initDb() gorp.DbMap {
    db, err := sql.Open("sqlite3", "db.sqlite3")
    checkErr(err, "sql.Open faild")
    dbmap := gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
    dbmap.AddTableWithName(Article{}, "articles").SetKeys(true, "Id")
    err = dbmap.CreateTablesIfNotExists()
    checkErr(err, "Create tables failed")
    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err.(*errors.Error).ErrorStack())
    }
}

func index (c *gin.Context) {
    content := gin.H{"Hello": "World"}
    c.JSON(200, content)
}

func ArticlesList(c *gin.Context) {
    var articles []Article
    _, err := dbmap.Select(&articles, "select * from articles order by article_id")
    checkErr(err, "Select failed")
    content := gin.H{}
    for k, v := range articles {
        content[strconv.Itoa(k)] =v
    }
    c.JSON(200, content)
}

func ArticlesDetail(c *gin.Context) {
    article_id := c.Params.ByName("id")
    a_id, _ := strconv.Atoi(article_id)
    article := getArticle(a_id)
    content := gin.H{"title": article.Title, "content": article.Content}
    c.JSON(200, content)
}

func ArticlePost(c *gin.Context) {
    var json Article

    c.Bind(&json)
    article := createArticle(json.Title, json.Content)
    if article.Title == json.Title {
        content := gin.H{
            "result": "Success",
            "title": article.Title,
            "content": article.Content,
        }
        c.JSON(201, content)
    } else {
        c.JSON(500, gin.H{"result": "An error occured"})
    }
}

func createArticle(title, body string) Article {
    article := Article{
        Created: time.Now().UnixNano(),
        Title: title,
        Content: body,
    }

    err := dbmap.Insert(&article)
    checkErr(err, "Insert failed")
    return article
}

func getArticle(article_id int) Article {
    article := Article{}
    err := dbmap.SelectOne(&article, "select * from articles where article_id=?", article_id)
    checkErr(err, "selectOne failed")
    return article
}

func main() {
    app := gin.Default()
    app.GET("/", index)
    app.GET("/articles", ArticlesList)
    app.POST("/articles", ArticlePost)
    app.GET("/articles/:article_id", ArticlesDetail)
    app.Run(":8000")
}
1

There are 1 answers

1
nouney On

The stacktrace is explicit: interface conversion: error is *errors.errorString, not *errors.Error.

So in checkErr(), the type assertion on err is wrong.

Here is the solution:

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err.(*errors.errorString).ErrorStack())
    }
}

If you need to understand why you are getting this panic, I'd suggest you to read the Golang specs about type assertions.