Setting up Route Not Found in Gin
Asked Answered
C

2

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"})
Clerihew answered 7/9, 2015 at 17:41 Comment(0)
T
74

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"})
})
Thermaesthesia answered 7/9, 2015 at 18:24 Comment(4)
Yes I've seen that but how do I make the gin context available?Clerihew
Have you seen NoRoute signature? It receives a Handler, which is then passed the Context. Is that what you mean?Thermaesthesia
Could you provide an example of what you mean using/passing the contextClerihew
Oh i see - that's straight forward. ThanksClerihew
W
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)
}
Washbowl answered 11/9, 2022 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.