I have the following Gin middleware:
func CheckAppId(appC *core.Context) gin.HandlerFunc {
return func(c *gin.Context) {
//get Basic Auth credentials
appId, token, _ := c.Request.BasicAuth()
if appId == "" {
c.JSON(http.StatusOK, gin.H{"code": "MISSING_APP_ID", "message": "Your request is missing an application id"})
return //this is being ignored???
}
c.Next() //this still gets hit
}
}
but if the appId == ""
the JSON gets returned and c.Next()
gets executed too. Is this expected behaviour?
EDIT I thought the problem had been sold but the same thing appears to be happening. I now have:
func CheckAppId(appC *core.Context) gin.HandlerFunc {
return func(c *gin.Context) {
//get Basic Auth credentials
appId, token, _ := c.Request.BasicAuth()
if appId == "" {
//I'm getting this JSON in the response
c.JSON(http.StatusOK, gin.H{"code": "MISSING_APP_ID", "message": "Your request is missing an application id"})
c.Abort()
}
//find app_id in database
app := core.App{}
err := appC.Database.C("apps").Find(bson.M{"id" : appId}).One(&app)
if err != nil { //no app found
//and I'm getting this JSON in the response
c.JSON(http.StatusOK, gin.H{"code": "INVALID_APP_ID", "message": "The application id provided could not be found"})
c.Abort()
}
c.Next()
}
}
In a call to the API I receive both "MISSING_APP_ID" Json and "INVALID_APP_ID" Json
Looking at the Gin API docs, you'll need to call context.Abort() instead of returning from your method.
Abort stops the system to continue calling the pending handlers in the chain. Let's say you have an authorization middleware that validates if the request is authorized if the authorization fails (the password does not match). This method (Abort()) should be called in order to stop the execution of the actual handler.
So in your specific case
if appId == "" {
c.JSON(http.StatusOK, gin.H{"code": "MISSING_APP_ID", "message": "Your request is missing an application id"})
c.Abort()
return
}
TL;DR
You need to do:
if condition {
c.Abort()
} else {
c.Next()
}
Explanation
(Note: this builds on @Landers answer)
c.Abort()
just sets some internal flags that mark the context in a certain way that indicates abnormal termination. It makes sense, it's a parameterless method that returns nothing, so you're only calling it for its side-effects.
Same thing with c.Next()
Because of go's control flow (no exceptions or jumps) its clear that these two methods don't take any immediate action but "set up the stage" for the next phase, imagine Gins code that calls your CheckAppId
func:
// gin code
doStuff()
ctx := getContext()
// this is your CheckAppId
nextAction := FindAction()
nextAction(&ctx)
// here the context is evaluated to see if you either abort or continue.
checkContextStatus(&ctx)
So, if you're calling c.Abort()
and then c.Next()
you're overriding the abort indications.