Passing data from handler to middleware after serving request

626 Views Asked by At

I have the following simple API in Go:

package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Call the handler
        next.ServeHTTP(w, r)

        // Retrieve custom data from the request object after the request is served
        customData := r.Context().Value("custom_data")
        fmt.Println("Custom data:", customData)
    })
}

func handler(w http.ResponseWriter, reqIn *http.Request) {
    reqIn = reqIn.WithContext(context.WithValue(reqIn.Context(), "custom_data", true))
}

func main() {
    r := mux.NewRouter()
    // Attach the middleware to the router
    r.Use(middleware)
    // Attach the handler to the router
    r.HandleFunc("/", handler).Methods("GET")
    http.ListenAndServe(":8080", r)
}

I expected the context in the middleware to be able to access the value of "custom_data", but it is not able to, returning for that context value. This happens even if I use Clone instead of WithContext for adding a value in the context of the request.

Looking around, specifically this post, if I instead use this as the handler:

func handler(w http.ResponseWriter, reqIn *http.Request) {
    req := reqIn.WithContext(context.WithValue(reqIn.Context(), "custom_data", true))
    *reqIn = *req
}

It works as expected. But modifying the *http.Request is not the norm.

My real question that I am trying to solve is; how can I pass information from the handler to the middleware?

  • Adding a value to the context of the *http.Request would be able to be accessed in the middleware.
1

There are 1 best solutions below

2
mkopriva On

You can do the following:

func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        custom_data := make(map[string]any)
        r = r.WithContext(context.WithValue(r.Context(), "custom_data", custom_data))

        // Call the handler
        next.ServeHTTP(w, r)

        // Retrieve custom data from the request object after the request is served
        v := r.Context().Value("custom_data")
        fmt.Printf("Custom data(%T): %v\n", v, v)

        // or use the above defined map directly
        fmt.Printf("Custom data(%T): %v\n", custom_data, custom_data)
    })
}

func handler(w http.ResponseWriter, r *http.Request) {
    m, ok := r.Context().Value("custom_data").(map[string]any)
    if ok && m != nil {
        m["value"] = true
    }
}