Home > OS >  Not able to get the keys of a Map even though gin's Context struct has Header as Map
Not able to get the keys of a Map even though gin's Context struct has Header as Map

Time:09-27

I'm not able to get all the keys from Gin's Context.Header (Golang's gin-gonic http/rest framework) field, even though Header is defined as a Map "type Header map[string][]string" (Header is from net\http\Header.go, request is from net\hhtp\request.go, Context is from Gin-gonic's package), and surprisingly it's strange that even at the compile/build time, Visual Studio code doesn't let me call/use "MapKeys()" method on this Header which is of map type (Given Golang is statically typed language and it knows its data type at compile time already).

  1. I need to copy all the HTTP headers into the Logger, so that when I log any message, I can put the corresponding request Headers.
  2. And also I need to pass all the HTTP Headers from HTTP to gRPC calls for end to end call traceability need.
func (l *Logger) InfoCtx(ctx *gin.Context, md metadata.MD) *zerolog.Event {
    headerName := "X-Request-Id" // Read all the headers from the ENV file
    // mapping := make(map[string]string)
    // mapping[headerName] = ctx.Request.Header[headerName][0]
    event := l.Logger.Info()
    
    // ctx.Request.Header ==> Even though this is a "map" type, 
    // which is known at the compilation time itself, 
    // it doesn't let me use any map functions.

    if ctx != nil && len(ctx.Request.Header[headerName]) > 0 {
        event = event.Str(headerName, ctx.Request.Header[headerName][0])

    } else if md != nil {
        // some other gRPC metadata context handling (not relevant for this question)
        
    }
    return event
}

Could you please help?

Header Object

Request object uses Header field

Shows Header is of map type

CodePudding user response:

I may be misunderstanding your issue but I'm able to enumerate the map of request headers from Gin's context:

go.mod:

module github.com/OWNER/stackoverflow/69315290

go 1.16

require github.com/gin-gonic/gin v1.7.4

And main.go:

package main

import (
    "log"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        for k, v := range c.Request.Header {
            log.Printf("%s: %v", k, v)
        }
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run()
}

And:

curl --header "dog: freddie" localhost:8080/ping

Yields:

{"message":"pong"}

And:

2021/09/25 10:41:05 User-Agent: [curl/7.68.0]
2021/09/25 10:41:05 Accept: [*/*]
2021/09/25 10:41:05 Dog: [freddie]
[GIN] 2021/09/25 - 10:41:05 | 200 |     408.631µs |       127.0.0.1 | GET      "/ping"

CodePudding user response:

The other approach that worked for me meanwhile was,

  1. I created a struct to have "HttpHeadersMap map[string][]string" in it
type CommonContext struct {
    HttpHeadersMap map[string][]string
    RequestContext context.Context

    GrpcMDHeadersMap map[string][]string
}
  1. Assigned Gin's "ctx.Request.Header" to "HttpHeadersMap map[string][]string"
func GetCommonCtx(ctx *gin.Context, md metadata.MD) CommonContext {
    var commonContext CommonContext
    if ctx != nil {
        // event = event.Str(headerName, ctx.Request.Header[headerName][0])
        commonContext = CommonContext{ // don't return address, use valye type
            HttpHeadersMap: ctx.Request.Header,
            RequestContext: ctx.Request.Context(),
        }
    }
...
}

then inside "gRPC Interceptor (just showing for example use case)", I could use it "HttpHeadersMap" regular way as "headersMapVal.MapKeys()" to iterate over the Map keys.

func clientInterceptor(
    ctx context.Context,
    method string,
    req interface{},
    reply interface{},
    cc *grpc.ClientConn,
    invoker grpc.UnaryInvoker,
    opts ...grpc.CallOption,
) error {
    start := time.Now()
    commonCtx := commonContext.GetCommonCtx(nil, metadata.MD{})
    if callOpt, ok := opts[0].(CustomDataCallOption); ok {

        headersMapVal := reflect.ValueOf(callOpt).FieldByName("HeadersMap")
        newMap := make(map[string]string)
        // allKeysMap := make(map[string]string)
        for _, key := range headersMapVal.MapKeys() {
            // fmt.Printf("headersMapVal.MapKeys(), e %v", e)
            // c_key := e.Convert(headersMapValueIndirectStr.Type().Key())
            keyValue := headersMapVal.MapIndex(key)

...
...
}

  • Related