WWTLF
WWTLF

Reputation: 540

go-swagger Oauth2 Authentication sample

https://github.com/go-swagger/go-swagger/blob/master/examples/oauth2/restapi/configure_oauth_sample.go

Could anyone explain what this piece of code is for?

// This demonstrates how to enrich and pass custom context keys.
// In this case, we cache the current responseWriter in context.
type customContextKey int8

const (
    _ customContextKey = iota
    ctxResponseWriter
)

// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares(handler http.Handler) http.Handler {
    ourFunc := func(w http.ResponseWriter, r *http.Request) {
        rctx := context.WithValue(r.Context(), ctxResponseWriter, w)
        handler.ServeHTTP(w, r.WithContext(rctx))
    }
    return http.HandlerFunc(ourFunc)

}

What for enrich and pass custom context keys?

Upvotes: 0

Views: 731

Answers (1)

Ezequiel Muns
Ezequiel Muns

Reputation: 7742

In condensed terms: This is applying a custom middleware to all the routes that your go-swagger app serves. That middleware adds the ResponseWriter as a custom value in the request context. To be sure, this has nothing to do with OAuth.

Going step by step:

The context is a special type that can carry request-scoped values, deadlines and cancellation signals across layers of your code.

type customContextKey int8

Here we're defining an unexported context key type. The reason we do that is that when we add our value to the context, we want that the value doesn't collide with values set by other packages that also may interact with the context. This could happen if we just used a string "customContextKey" for example and some other package happened to use the same string. More info here.

const (
    _ customContextKey = iota
    ctxResponseWriter
)

Here we're creating a customContextKey value named ctxResponseWriter with value 1. Note that we're ignoring (_) the first value of the iota which is 0, and instead using the next value, 1. This is the type-safe key to be used to store the actual ResponseWriter value in the context.

Middlewares are functions that take a http.Handler and return a http.Handler, they can be composed together and are a pattern to add extra functionality to the application handler/s in a generic way. For a deeper understanding check Making and Using HTTP Middleware.

func setupMiddlewares(handler http.Handler) http.Handler {
    ourFunc := func(w http.ResponseWriter, r *http.Request) {
        rctx := context.WithValue(r.Context(), ctxResponseWriter, w)
        handler.ServeHTTP(w, r.WithContext(rctx))
    }
    return http.HandlerFunc(ourFunc)
}

The function here wraps the given handler in such a way that:

  • the context is pulled out of the request — r.Context()
  • "enriched" with our new key and the w ResponseWriter value — context.WithValue(..., ctxResponseWriter, w)
  • the request's context is replaced with the updated context — r.WithContext(rctx)
  • the wrapped handler is run with this updated request — handler.ServeHTTP(w, ...)

Inside some http.Handler you might then extract the value like this:

func someHandler(w http.ResponseWriter, r *http.Request) {
    value, ok := r.Context().Value(ctxResponseWriter).(http.ResponseWriter)
    if ok {
        // we have the value!
    }
}

This must be just an example of usage. It's a quite silly to pass the ResponseWriter like this, since it's already passed in as a parameter to any http.Handler as you can see above, and depending on it from deeper layers of your app is poor design. A better use for this might be for passing a request-scoped logger object, for example.

Upvotes: 1

Related Questions