Skip to content

Gin adaptor middlewares#535

Open
evilenzo wants to merge 3 commits intogo-fuego:mainfrom
evilenzo:gin_adaptor_middlewares
Open

Gin adaptor middlewares#535
evilenzo wants to merge 3 commits intogo-fuego:mainfrom
evilenzo:gin_adaptor_middlewares

Conversation

@evilenzo
Copy link
Copy Markdown
Contributor

@evilenzo evilenzo commented Jun 5, 2025

Resolves #534 (more details there)

@evilenzo
Copy link
Copy Markdown
Contributor Author

evilenzo commented Jun 5, 2025

So I decided to simplify task with such separation:

  1. Only default http middlewares for default server
  2. Only gin middlewares for gin server

@dylanhitt
Copy link
Copy Markdown
Collaborator

Is there not a way to convert func(http.Handler) http.Handler into gin.Context? 🤔

@dylanhitt
Copy link
Copy Markdown
Collaborator

ehhhh, i guess it does make sense that each adaptors community would want to use their adaptors. If someone wanted to use a fuego middleware wrapping themselves wouldn't be very difficult as pretty much every community has a func to wrap http.Handler...

@dylanhitt
Copy link
Copy Markdown
Collaborator

Really interested in what others think as well.

@evilenzo
Copy link
Copy Markdown
Contributor Author

evilenzo commented Jun 6, 2025

Is there not a way to convert func(http.Handler) http.Handler into gin.Context? 🤔

Indeed there is some functions for conversion (but no function for such conversion. You can try it yourself in some playground). I explained it in issue more detailed but my English might be bad so...

We have two types of middlewares:

  1. func (http.Handler) http.Handler - fuego/http
  2. func (c *gin.Context) - gin

Before this PR all middlewares was stored as func (http.Handler) http.Handler and it creates two problems with gin:

  1. OK, we can convert func (http.Handler) http.Handler to func (c *gin.Context) when applying middlewares to gin router. Not a big problem (but still a problem cause gin only has gin.WrapH and gin.WrapF functions that doesn't work with such types of middlewares. More details there Using http.Handler? gin-gonic/gin#293)
  2. But we also want users to be able to use their gin middlewares func (c *gin.Context). And this is a major problem. Imagine we create some custom option to pass such middleware. But we store all middlewares as func (http.Handler) http.Handler. So we have to convert gin middleware to http middleware. That's the first conversion (and it's not an easy task. It has runtime overhead cause it's wrapping). Then we want to apply this middleware to gin. And we have to convert http middleware to gin middleware. That's the second conversion. So to use one gin middleware we must convert it twice and gain runtime overhead. I don't think that such solution is reasonable.

On the other hand, if you are ok that we store middlewares as []any but you think that we should also have ability to apply both types of middlewares to gin... I don't think that combining different middlewares by our hands is a good idea since even gin doesn't have conversion from func (http.Handler) http.Handler middleware to it's own

@dylanhitt
Copy link
Copy Markdown
Collaborator

dylanhitt commented Jun 6, 2025

To respond to

OK, we can convert func (http.Handler) http.Handler to func (c *gin.Context) when applying middlewares to gin router. Not a big problem (but still a problem cause gin only has gin.WrapH and gin.WrapF functions that doesn't work with such types of middlewares. More details there gin-gonic/gin#293)

It is possible though. It's not prettiest thing. I was looking at how the adaptor from go-fiber does it:

return func(c *gin.Context) {
        handler := mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            c.Request = r
            c.Writer = &ginResponseWriter{ResponseWriter: w, ginWriter: c.Writer}
            c.Next()
        }))
        handler.ServeHTTP(c.Writer, c.Request)
    }

I believe we'd have to set reset the headers up as well. So this code isn't complete. The trade off here is that we would need to do this for every adaptor going forward.


The larger concern that I have here is the other way. Converting gin middleware to func (http.Handler) http.Handler. It's not clear to me how that would work. Kinda defeats the point of using gin middleware at all..

Edit: Ope. Just read your second point. I see we're pretty aligned there.

@dylanhitt
Copy link
Copy Markdown
Collaborator

This kinda lends to my thought here as well: #394 (comment)

Trying to dictate too much on how configure each adaptors routes/server would cripple this project.

@dylanhitt
Copy link
Copy Markdown
Collaborator

Was thinking about this some. Maybe we should wrap our BaseRoute with GinRoute

@evilenzo
Copy link
Copy Markdown
Contributor Author

evilenzo commented Jun 9, 2025

Maybe we should wrap our BaseRoute with GinRoute

Can you please provide some example? Can't understand the idea

@dylanhitt
Copy link
Copy Markdown
Collaborator

dylanhitt commented Jun 9, 2025

Right now a gin handler will return

func GetGin(engine *fuego.Engine, ginRouter gin.IRouter, path string, handler gin.HandlerFunc, options ...func(*fuego.BaseRoute)) *fuego.Route[any, any, any] {
	return handleGin(engine, ginRouter, http.MethodGet, path, handler, options...)
}
*fuego.Route[any, any, any] 

My though is to wrap this like:

type GinRoute[ResponseBody any, RequestBody any, Params any] struct {
	Route[any, any, any]

	middlewares []func(c *gin.Context)
}

// Route is the main struct for a route in Fuego.
// It contains the OpenAPI operation and other metadata.
// It is a wrapper around BaseRoute, with the addition of the response and request body types.
type Route[ResponseBody any, RequestBody any, Params any] struct {
	BaseRoute
}

Not really sure how much would need to change though tbh. It was just a passing thought on a Saturday morning so it will need to be investigated.

@evilenzo
Copy link
Copy Markdown
Contributor Author

evilenzo commented Jun 9, 2025

Hm... I see no problem with Route[...]. Problem is a little bit down the flow (with fuego.BaseRoute). Look at the options:

options ...func(*fuego.BaseRoute)

Main problem here is they are applied to fuego.BaseRoute. Only one thing that uses it is NewBaseRoute() and I don't know if we can change much there.

func handleFuego[T, B, P any](engine *fuego.Engine, ginRouter gin.IRouter, method, path string, fuegoHandler func(c fuego.Context[B, P]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B, P] {
	baseRoute := fuego.NewBaseRoute(method, ginToFuegoRoute(path), fuegoHandler, engine, options...)
	// ...
}

We want to apply option to gin but we can apply it only to BaseRoute

@evilenzo
Copy link
Copy Markdown
Contributor Author

@dylanhitt @EwenQuim sorry, but I still can't understand your idea... It would be cool if you provide some draft/MRE so I can finish this PR

@roskee
Copy link
Copy Markdown

roskee commented Apr 8, 2026

@evilenzo any plans to make this work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gin compatibility layer per route middlewares

3 participants