package utils import ( "context" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" ) // HeadersOptions 控制请求头透传的差异层。基础两层恒有: // 1. 透传 HTTP 请求头(含 Authorization/X-User-Info) // 2. X-User-Info 为空且 ctx 携带 user(恢复续跑/异步任务注入的合成用户)时补 X-User-Info type HeadersOptions struct { ResolveToken bool // X-User-Info 仍为空时,解析调用方 token 得到用户注入(直连场景) TokenFromQuery bool // Authorization 为空时从 URL query ?token= 补 Bearer(WS 握手) } // HeadersFromCtx 统一构造调用方请求头透传 map。全项目唯一的拼头入口 // // 放这里而非 common/http:本函数只依赖 GetUserInfo(同包),与 gclient/gsvc/consul/jaeger 客户端 // 基础设施零耦合;common/http 空导入 common/consul、common/jaeger(init 里 g.Cfg().MustGet 无默认值, // 无配置文件环境会 panic),common/oss 等消费方特意避开它,放 utils 依赖链干净。 func HeadersFromCtx(ctx context.Context, opts ...HeadersOptions) map[string]string { op := HeadersOptions{} if len(opts) > 0 { op = opts[0] } headers := make(map[string]string) if r := g.RequestFromCtx(ctx); r != nil { for k, v := range r.Request.Header { if len(v) > 0 { headers[k] = v[0] } } } if headers["X-User-Info"] == "" { if u := ctx.Value("user"); !g.IsNil(u) { headers["X-User-Info"] = gconv.String(u) } } if op.TokenFromQuery && headers["Authorization"] == "" { if r := g.RequestFromCtx(ctx); r != nil { if t := r.Request.URL.Query().Get("token"); t != "" { headers["Authorization"] = "Bearer " + t } } } if op.ResolveToken && headers["X-User-Info"] == "" { if user, err := GetUserInfo(ctx); err == nil && user != nil { headers["X-User-Info"] = gconv.String(user) } } return headers }