-
Notifications
You must be signed in to change notification settings - Fork 8.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gin.Context is not a interface, how to extend it? #1123
Comments
What's your scenario? I think context is the core part of gin that coordinates other components, like controls the middlewares execution, and model binding...etc. It'll make the system more complicated if you change the default behavior of such core components. |
do you mean you want to share service for all request handlers globally? |
@ycavatars Interface makes it possible to extend context, for example, you can add UserID field to context, and call context.UserID directly instead of using context.GetString("UserID"). |
any news? :) |
Here's some input about supporting custom Context in Gin. I'm not very experienced so take it with a grain of salt. What I expectedtype MyContext struct {
*gin.Context
// Value set in my custom middleware
User *AuthenticatedUser
}
func indexHandler (c *MyContext) {
fmt.Print(c.User)
} Advantages: ✅ c.User is typed What Gin and almost all libs including standard lib providesInstead Gin, and most libs, force me to do something along the lines of: func indexHandler (c *Context) {
user, ok =: c.Get("user").(*AuthenticatedUser)
if ok {
fmt.Print(user)
} else {
// handle case when "user" key was not found somehow
}
} ❌ bloat *Note that we are not supposed to use strings as keys in stuff like that, which alleviates some of the problems. Although there's code on Gin's frontpage using string as key (https://i.imgur.com/Kdbr9rg.png), perhaps for simplicity sake. This part of https://golang.org/pkg/context/#WithValue provides some reasoning on why not using string is good practice:
Mitigating Context map problemsThere are ways to mitigate some of the bloat. One is encapsulating the typecasting logic inside helper functions (aka shove the bloat somewhere only we know), typically contained inside an App struct so we don't pollute global scope: func (app *App) User(c *gin.Context) *AuthenticatedUser {
if user, ok =: c.Get("user").(*AuthenticatedUser); ok {
return user
}
return &AuthenticatedUser{} // Or perhaps nil
} So now our handlers look something along the lines of: func (app *App) indexHandler (c *gin.Context) {
fmt.Print(app.User(c))
} To me as a lib user, Context's
I'm going to study the problem further. |
After some careful consideration I decided to go with the helper() solution described in Mitigating Context map problems. Some considerations:
|
One bad thing of gin.Context is that it's hard to cooperate with standard Context. func UserHandler(gc *gin.Context) {
ctx := context.WithValue(gc, "user_defined_key", "user_value")
//or context.WithTimeout context.WithDeadline etc
UserFunc1(ctx)
}
func UserFunc1(ctx context.Context) {
//do something with user_defined_key
v, ok := ctx.Value("user_defined_key").(string)
//.....
//we can never get gin.Context back anyway...
} so, I modify the gin.Context, to make it more compatible with standard context: func UserHandler(gc *gin.Context) {
ctx := context.WithValue(gc, "user_defined_key", "user_value")
UserFunc1(ctx)
}
func UserFunc1(ctx context.Context) {
//do something with user_defined_key
v, ok := ctx.Value("user_defined_key").(string)
//.....
gc := gin.MustGinContext(ctx)
//to do things with gin.Context as need
} |
I encounter same problem ,I need to get gin.Context work with OpenTracing client packet which work with context.Context,but cannot get gin.Context back after process...hope I can simply get gin.Context from context.Context,at least provide some method can merge value data from context.Context to gin.Context. span, ctx := opentracing.StartSpanFromContext(c, opName)
// c.with
defer span.Finish()
span.LogFields(
log.String("event", "soft error"),
log.String("type", "cache timeout"),
log.Int("waited.millis", 1500))
h(ctx.(*gin.Context))//can get the context back to gin.context since it is actually valueCtx struct type |
well fine,it looks like almost impossible to copy all value from standard context since valueCtx is private recursive structure...especially the key is private type in opentracing...I can't even use the key manually outside that package. I hope there is any way to pass regular context's Values though gin.context. |
ok I found that I can pass context though Request's ctx ,not pretty but it should work |
One approach that I learned when researching heterogeneous type-safe map implementations in Java was to put the method for converting the type as a method on the Key. Unfortunately that does very little good with |
Just brainstormig: now that we have generics it would be nice to have gin.Context as a type constraint and implement our custom context types like type MyContext struct {
gin.BaseContext // this itself satisfy the constraint
MyUser *User
} then I could register that type somewhere when initialising the gin.Engine and it would be passed around instead of the current gin.Context. (Not 100% sure how to do this in Go 1.18) Of course every requests starts with a fresh context so it's my (the gin user) responsibility to fill that MyUser field somehow (i.e. in a middleware) btw the helper function can be a little more generic now: func GetValue[T any](c *gin.Context, key string) T {
v, ok := c.Get(key)
if !ok {
panic("key not found in context")
}
return v.(T)
} |
A way to do what @Pitasi is suggesting and configure the custom stuff is to provide a
Or something similar, then we could register a custom This kind of refactoring should not break anything because it is just adding a new method and not removing anything and if the default behavior stays the same it would continue to work with existing code. A default |
That is what |
*2023? *almost |
|
The option to provide a custom context producer would be incredibly useful. |
#3963 I think that should solve some of the problem. |
gin.Context is not a interface, how to extend it?
The text was updated successfully, but these errors were encountered: