[net/http] Golang如何发送string类型的Header值而非[]string

[net/http] Golang如何发送string类型的Header值而非[]string net/http 库中的 Header 结构体格式为 map[string][]string,是否有任何可能的方法使用该库来设置 map[string]string 类型的头部?主机无法解析 Authorization 头部值,因为它是一个映射而不是字符串,导致返回 401 错误。

4 回复

问题是我代码中的一个错误,idtoken.NewClient 被误用,这个库会获取一次令牌并自动设置请求头。使用同一个客户端进行其他调用会因令牌错误而导致授权失败,因此服务器返回 401。

更多关于[net/http] Golang如何发送string类型的Header值而非[]string的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


http.Header 只是 Go 中定义的内存类型。当 HTTP 请求实际发送时,所有头部信息都只是请求消息的一部分,它们看起来与其他任何 HTTP 请求头部一样:

Content-Type: application/json
Accept: application/json
Authorization: basic abc123...

因此,服务器并不知道它们原本是 []string 类型还是 string 类型。

HTTP 标准允许通过用逗号连接值或重复字段名的方式,将多个值与单个字段关联。如果你将值放入切片中,在生成请求时,上述两种情况之一将会发生。

考虑到你提到了解析 Authorization 头部值的问题,我猜测(如果我猜错了,能否请你提供你正在使用的代码、服务器上的错误信息等任何能帮助我们为你排查问题的内容)问题在于你将 Authorization 方案和值作为切片中独立的值进行设置(例如 req.Headers["Authorization"] = []string{"basic", "abc123..."}),这导致 Authorization 头部字段显示为 Authorization: basic, abc123...

Authorization: basic
Authorization: abc123...

服务器可能无法正确解析这种格式。你应该使用 Set 方法将 Authorization 值设置为单个字符串:

req.Header.Set("Authorization", "basic abc123...")

如果这没有解答你的问题,请提供更多信息,例如你的代码、服务器的错误信息等,以便我们帮助你。slight_smile

skillian:

req.Header.Set("Authorization", "basic abc123...")

感谢肖恩的回复。令牌是有效的,我已经用 Python 的 requests 库测试过了。看起来使用 curl 也会失败,所以使用 net/http 应该没有问题。

请求代码

header := http.Header{}
header.Add("Content-Type", "application/json")
header.Set("Authorization", "Bearer "+t)
resp, err := http.Client.Do(&http.Request{
		Method: "GET",
		URL:    url,
		Header: header,
	})

代码响应

 "{\n  \"error\": {\n    \"code\": 401,\n   
 \"message\": \"Request had invalid authentication credentials. Expected OAuth 2 access token, 
login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.\",\n 
\"status\": \"UNAUTHENTICATED\"\n  }\n}\n"

net/http 工作正常,我执行了 curl -v 命令,看起来我得到了下面这个错误。

Curl 响应

< www-authenticate: Bearer realm="https://accounts.google.com/", error="insufficient_scope", scope="SOME_SCOPE">
{
  "error": {
    "code": 403,
    "message": "Request had insufficient authentication scopes.",
    "status": "PERMISSION_DENIED"
}

不知何故,我在 curl 中绕过了 401 错误,但在我的代码中却没有。一旦我修复了 curl 的响应,问题就应该解决了。

在Go的net/http库中,Header类型确实是map[string][]string,但你可以通过以下方式设置单个字符串值的头部:

package main

import (
    "net/http"
)

func main() {
    // 创建请求
    req, err := http.NewRequest("GET", "https://api.example.com", nil)
    if err != nil {
        panic(err)
    }

    // 方法1:直接设置字符串值(推荐)
    req.Header.Set("Authorization", "Bearer your-token-here")

    // 方法2:使用Add方法(也会自动处理为字符串)
    req.Header.Add("Authorization", "Bearer your-token-here")

    // 方法3:直接赋值(底层会自动转换为[]string)
    req.Header["Authorization"] = []string{"Bearer your-token-here"}

    // 发送请求
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

对于接收到的请求,读取头部值:

func handler(w http.ResponseWriter, r *http.Request) {
    // 获取Authorization头部(返回字符串)
    authHeader := r.Header.Get("Authorization")
    
    // 或者获取整个切片
    authHeaders := r.Header["Authorization"]
    if len(authHeaders) > 0 {
        firstAuth := authHeaders[0] // 第一个值
    }
}

关键点:

  1. Header.Set() 方法会自动将字符串值转换为[]string
  2. 服务器端使用r.Header.Get()获取的是字符串(返回第一个值)
  3. 即使底层是[]string,HTTP协议规范允许单个头部字段有多个值

如果服务器返回401错误,可能是以下原因:

  • 令牌格式不正确
  • 令牌已过期
  • 服务器期望不同的认证方案

调试建议:

// 打印实际发送的头部
fmt.Printf("Authorization: %v\n", req.Header["Authorization"])

大多数HTTP客户端和服务器都能正确处理这种格式,因为Header.Set()方法已经处理了类型转换。

回到顶部