You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang跨服务传递Context值的方法及认证场景选型咨询

问题描述

我正在学习Golang,遇到了Context相关的问题:我希望在Server1向Server2发起请求时传递Server1设置的Context值,但Server2的Context中无法获取该值。测试时用curl请求http://localhost:8080,Server1控制台输出“Send request to http://localhost:8082”,Server2控制台输出“request coming in”和<nil>。请问如何让Server2获取Server1设置的Context值?同时想了解这种方式是否适合传递认证类数据,还是Middleware模式更合适?

Server1 代码

package main

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

    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()
    r.HandleFunc("/", hello)

    fmt.Println("Starting listening on port 8080...")
    http.ListenAndServe(":8080", r)
}

// Context's key.
type Sample string

var sampleKey Sample = "sample"

func hello(w http.ResponseWriter, r *http.Request) {

    fmt.Println("Send request to http://localhost:8082")
    ctx := context.WithValue(context.Background(), sampleKey, "1234")
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8082", nil)
    if err != nil {
        fmt.Println("Error while sending request: ", err)
    }

    // Send request.
    var c http.Client
    c.Do(req)
}

Server2 代码

package main

import (
    "fmt"
    "net/http"

    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()

    r.HandleFunc("/", receive)
    fmt.Println("Start listening on port 8082...")
    http.ListenAndServe(":8082", r)
}

type Sample string

var sampleKey Sample

func receive(w http.ResponseWriter, r *http.Request) {
    fmt.Println("request coming in")
    fmt.Println(r.Context().Value(sampleKey))
}

解决方案

一、跨服务传递Context值的问题解决

Golang的context.WithValue仅支持同一个进程内的调用链传递数据,Server1和Server2是两个独立进程,HTTP请求不会自动携带进程内的Context数据——这是你获取<nil>的核心原因。

要实现跨服务数据传递,需要把Context中的值通过HTTP请求的自定义Header传递,再在Server2中读取Header值(也可存入Context供后续使用)。修改代码如下:

修改后的Server1核心代码

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Send request to http://localhost:8082")
    req, err := http.NewRequest(http.MethodGet, "http://localhost:8082", nil)
    if err != nil {
        fmt.Println("Error while sending request: ", err)
        return
    }
    // 将需要传递的值放入HTTP Header
    req.Header.Set("X-Sample-Data", "1234")

    var c http.Client
    c.Do(req)
}

修改后的Server2核心代码

func receive(w http.ResponseWriter, r *http.Request) {
    fmt.Println("request coming in")
    // 从Header读取传递的值
    val := r.Header.Get("X-Sample-Data")
    fmt.Println(val) // 输出 "1234"

    // 若需存入Context供后续逻辑使用
    type Sample string
    var sampleKey Sample = "sample"
    ctx := context.WithValue(r.Context(), sampleKey, val)
    // 后续处理可直接使用ctx
}

另外,你原Server2代码中sampleKey未赋值(默认是空字符串),即使进程内传递也无法获取值,但核心问题还是跨进程Context无法直接传递。

二、认证类数据的传递方式选择

不推荐用进程内Context跨服务传递认证数据

进程内Context的设计初衷是在同进程调用链中传递请求域数据(如超时信号、请求ID),跨服务场景完全无效,必须依赖HTTP Header、URL参数或请求体传递数据。

推荐使用Middleware模式处理认证

对于JWT Token、API Key这类认证数据,Middleware模式是更优选择:

  • 逻辑统一:请求进入业务Handler前,Middleware统一从Header(如Authorization)读取认证数据,完成合法性校验后,将解析后的用户信息存入Context,后续Handler可直接取用。
  • 复用性强:所有需认证的接口都能复用同一个Middleware,避免重复编写认证逻辑。
  • 安全性可控:Middleware可统一处理认证失败场景(如返回401状态码),避免逻辑分散。

示例:Server2的认证Middleware实现

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        // 模拟Token校验逻辑
        if token == "" || token != "valid-jwt-token" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        // 将用户ID存入Context
        ctx := context.WithValue(r.Context(), "user_id", "1001")
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// 路由中挂载Middleware
func main() {
    r := chi.NewRouter()
    r.Use(AuthMiddleware) // 全局生效,也可针对特定路由组挂载
    r.HandleFunc("/", receive)
    fmt.Println("Start listening on port 8082...")
    http.ListenAndServe(":8082", r)
}

func receive(w http.ResponseWriter, r *http.Request) {
    userID := r.Context().Value("user_id").(string)
    fmt.Println("Authenticated user ID:", userID)
}

内容的提问来源于stack exchange,提问作者Jassy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 05:50:11