grpc-ecosystem / grpc-gateway

gRPC to JSON proxy generator following the gRPC HTTP spec
https://grpc-ecosystem.github.io/grpc-gateway/
BSD 3-Clause "New" or "Revised" License
18.23k stars 2.24k forks source link

Feat: add http handler middleware #4319

Open nikitaksv opened 6 months ago

nikitaksv commented 6 months ago

🚀 Feature

PR

4290

Problem

The grpc-gateway functionality supports REST ednpoint registration directly to ServeMux without using GRPC-server. Accordingly, interceptors for grpc are not called and HTTP route pattern is set at the moment of service method call. So we have to wrap ServeMux in custom middleware for logging, metrics, authorization and the like. The solution works quite well, but it has 2 significant drawbacks:

  1. the ability to determine an existing route before the middleware is triggered is lost. That is, the API client can send requests via non-existent routes and these requests will be logged, authorized and generally perform any logic when there is no need to do so.

    // register POST /hello-world
    err := helloworld.RegisterHelloWorldServiceHandlerServer(context.Background(), serveMux, service)
    if err != nil {
    panic(err)
    }
  2. In middleware there is no possibility to get http pattern of the route before the request is executed. Pattern is placed in context.Context only after execution of the generated service method code. In fact, we get access to the route only after the request is processed.

    func CustomMiddleware() func(next http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            pattern, ok := runtime.HTTPPathPattern(r.Context()) // path pattern NOT exist in context
            next.ServeHTTP(w, r)
            pattern, ok := runtime.HTTPPathPattern(r.Context()) // path pattern exist in context
        })
    }
    }

Solution

  1. Add Middleware(HandlerFunc) HundlerFunc type to the runtime package
  2. Add a middlewares slice to the ServeMux structure and when adding a new route, apply middlewares to the route handler.
  3. The struct pattern implements the String method and prints the path of the route, so it would be logical to put this object in context.Context to access it from middleware
johanbrandhorst commented 6 months ago

Thanks for the issue! I'm sympathetic to this, but I'm worried that it will cause confusion to users who are not using the direct-to-implementation pattern. It's cool that this makes it easier to use the direct-to-implementation functionality, but we have to be careful in the implementation of the new API and options to avoid confusing users.