Setting a request header with or without Context in Golang I have two ways (middleware) of setting a specific header (if not set) to request but want to know if one is better/beneficial than the other and the reasons why.

Note: At some point I will need to log X-Request-Id value in every single application log I have. If this plan affects your answer, I don't know. Just saying!

Context version

package middleware

import (
    "context"
    "github.com/google/uuid"
    "net/http"
)

type CtxKey string
const CtxReqIdKey CtxKey = "X-Request-Id"

func RequestId(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
        CtxKey := req.Header.Get("X-Request-Id")
        if CtxKey == "" {
            CtxKey = uuid.New().String()
        }

        ctx1 := context.WithValue(req.Context(), CtxReqIdKey, CtxKey)
        ctx2 := req.WithContext(ctx1)

        handler.ServeHTTP(res, ctx2)
    })
}

# Elsewhere: request.Context().Value(middleware.CtxReqIdKey)


Without context version

package middleware

import (
    "github.com/google/uuid"
    "net/http"
)

func RequestId(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
        if req.Header.Get("X-Request-Id") == "" {
            req.Header.Set("X-Request-Id", uuid.New().String())
        }

        handler.ServeHTTP(res, req)
    })
}


Usage

err := http.ListenAndServe(":8080", middleware.RequestId(Router))
Long form comment:  Yes, the context version.  I'd consider it a bad pattern to modify incoming data inline (because it's harder to track later what came in and what the application did with it).  Especially in applications which do not only handle HTTP traffic, but also, say, gRPC calls, it's even more important that this data isn't stored for one type of handler only, but for all of them in a unified fashion.  Plus, context is (for better and worse) a kitchen sink for all layers of the code, even in the deepest layers you'll still be able to get that ID for various purposes, maybe auditing, or so.

Maybe it doesn't even need to go into the context, simply add it to the logger data (if there's such a thing), or however else that's being populated.  Doing that in a wrapper like this will again allow you to keep it out of the rest of the application - problem solved and no need to mess with the context object.  In any case it's not difficult to change that later.