34

I've setup a default router and some routes in Gin:

router := gin.Default()
router.POST("/users", save)
router.GET("/users",getAll)

but how do I handle 404 Route Not Found in Gin?

Originally, I was using httprouter which I understand Gin uses so this was what I originally had...

router.NotFound = http.HandlerFunc(customNotFound)

and the function:

func customNotFound(w http.ResponseWriter, r *http.Request) {
    //return JSON
    return
}

but this won't work with Gin.

I need to be able to return JSON using the c *gin.Context so that I can use:

c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
Zoyd
  • 3,449
  • 1
  • 18
  • 27
tommyd456
  • 10,443
  • 26
  • 89
  • 163

2 Answers2

71

What you're looking for is the NoRoute handler.

More precisely:

r := gin.Default()

r.NoRoute(func(c *gin.Context) {
    c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
})
Pablo Fernandez
  • 103,170
  • 56
  • 192
  • 232
3

Adding to what Pablo Fernandez wrote I also seen that the same can be done with 405 MethodNotAllowed, so similarly if for 404 we've got NoRoute for 405 there is NoMethod method

So having this as result


import (
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)


func main() {

   app := gin.New()
    app.NoRoute(func(c *gin.Context) {
        c.JSON(http.StatusNotFound, gin.H{"code": "PAGE_NOT_FOUND", "message": "404 page not found"})
    })

    app.NoMethod(func(c *gin.Context) {
        c.JSON(http.StatusMethodNotAllowed, gin.H{"code": "METHOD_NOT_ALLOWED", "message": "405 method not allowed"})
    })

}

In order to add a 'catch all' method we can do:


import (
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)


func main() {

   app := gin.New()
    app.NoRoute(func(c *gin.Context) {
        c.JSON(http.StatusNotFound, gin.H{"code": "PAGE_NOT_FOUND", "message": "404 page not found"})
    })

    app.NoMethod(func(c *gin.Context) {
        c.JSON(http.StatusMethodNotAllowed, gin.H{"code": "METHOD_NOT_ALLOWED", "message": "405 method not allowed"})
    })

}

    // Set-up Error-Handler Middleware
    app.Use(func(c *gin.Context) {
        log.Printf("Total Errors -> %d", len(c.Errors))

        if len(c.Errors) <= 0 {
            c.Next()
            return
        }

        for _, err := range c.Errors {
            log.Printf("Error -> %+v\n", err)
        }
        c.JSON(http.StatusInternalServerError, "")

    })


Now, gin has these 2 method only for these 2 type of errors,my guess is because are the most common one while building an API and wanted to add some default handler when you first set up the application.

In fact, we can see the implementation here:

// NoRoute adds handlers for NoRoute. It returns a 404 code by default.
func NoRoute(handlers ...gin.HandlerFunc) {
    engine().NoRoute(handlers...)
}

// NoMethod is a wrapper for Engine.NoMethod.
func NoMethod(handlers ...gin.HandlerFunc) {
    engine().NoMethod(handlers...)
}

Now, the body that uses by default when these 2 handlers are not used by who uses the gin framework (so the default one are) are defined enter link description here

var (
    default404Body = []byte("404 page not found")
    default405Body = []byte("405 method not allowed")
)

And then later on used on the function handleHTTPRequest from line 632

    if engine.HandleMethodNotAllowed {
        for _, tree := range engine.trees {
            if tree.method == httpMethod {
                continue
            }
            if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
                c.handlers = engine.allNoMethod
                serveError(c, http.StatusMethodNotAllowed, default405Body)
                return
            }
        }
    }
    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
}
Federico Baù
  • 6,013
  • 5
  • 30
  • 38