
go 的 `net/http` 默认不自动解压 `deflate` 编码响应,直接读取压缩字节会导致 `json.unmarshal` 报错“invalid character 'x' looking for beginning of value”;需手动检测 `content-encoding` 并使用 `compress/flate` 解压后再解析。
在 Go 中调用返回 Content-Encoding: deflate 的 HTTP 接口时,若未显式解压响应体,ioutil.ReadAll(res.Body)(或 io.ReadAll)将返回原始压缩字节流(如以字节 120 156 开头的 DEFLATE 数据),而非可读 JSON 字符串。此时 json.Unmarshal 尝试解析二进制数据,自然报错:invalid character 'x' looking for beginning of value(x 即 ASCII 120,是 DEFLATE 流的典型魔数)。
Go 标准库对 gzip 有自动支持(只要启用了 http.Transport 的 RegisterProtocol 或使用默认客户端且服务端发 gzip),但对 deflate 不提供自动解压——这是本问题的根本原因。
✅ 正确做法:手动识别并解压 deflate
需检查响应头 Content-Encoding,若为 deflate,则用 compress/flate 包解压。注意:HTTP 中的 deflate 指 RFC 1950 zlib 格式(含 zlib header),而 compress/flate 默认读取的是 RFC 1951 raw deflate 流。因此必须使用 zlib.NewReader(compress/zlib)而非 flate.NewReader。
以下是修复后的完整示例(适配 Go 1.16+,使用 io.ReadAll 替代已弃用的 ioutil.ReadAll):
package main
import (
"compress/zlib"
"encoding/json"
"fmt"
"io"
"net/http"
)
type Top struct {
Timestamp Timestamp `json:"timestamp"`
String_timestamp string `json:"string_timestamp"`
Monitor_status string `json:"monitor_status"`
}
type Timestamp struct {
Tv_sec int `json:"tv_sec"`
Tv_usec int `json:"tv_usec"`
}
func get_content() error {
url := "http://172.17.0.31:20000/top"
res, err := http.Get(url)
if err != nil {
return fmt.Errorf("HTTP GET failed: %w", err)
}
defer res.Body.Close()
// 检查 Content-Encoding 并解压
var reader io.ReadCloser = res.Body
if res.Header.Get("Content-Encoding") == "deflate" {
z, err := zlib.NewReader(res.Body)
if err != nil {
return fmt.Errorf("failed to create zlib reader: %w", err)
}
reader = z
}
body, err := io.ReadAll(reader)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}
var jsondata Top
if err := json.Unmarshal(body, &jsondata); err != nil {
return fmt.Errorf("JSON unmarshal failed: %w (raw bytes: %q)", err, body[:min(len(body), 64)])
}
fmt.Printf("Parsed: %+v\n", jsondata)
return nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
if err := get_content(); err != nil {
panic(err)
}
}⚠️ 关键注意事项
- zlib vs flate:HTTP deflate 使用 zlib 封装格式(RFC 1950),务必用 compress/zlib.NewReader;误用 compress/flate.NewReader 会因缺少 zlib header 而解压失败。
-
资源清理:zlib.Reader 实现了 io.ReadCloser,但其 Close() 不关闭底层 res.Body。因此仍需 defer res.Body.Close(),并在解压后显式关闭 zlib.Reader(上例中 reader 是 zlib.Reader 类型,io.ReadAll 不会自动关闭它)。更健壮写法:
if res.Header.Get("Content-Encoding") == "deflate" { z, err := zlib.NewReader(res.Body) if err != nil { return err } defer z.Close() // ? 必须关闭 zlib reader reader = z } -
其他编码兼容性:生产环境建议同时支持 gzip 和 deflate:
switch enc := res.Header.Get("Content-Encoding"); enc { case "gzip": reader, _ = gzip.NewReader(res.Body) defer reader.Close() case "deflate": reader, _ = zlib.NewReader(res.Body) defer reader.Close() } - 错误处理:避免 panic,改用 error 返回和日志,便于调试与集成。
✅ 总结
Go 的 http.Client 不自动处理 deflate 编码,开发者需主动检测响应头、选择正确的解压器(compress/zlib)、妥善管理资源。掌握此模式后,即可稳定对接各类压缩 HTTP API,避免因字节流误读导致的 JSON 解析失败。










