如何正确解码和回传 JSON 对象到前端(Go 语言实践指南)

本文详解 go 中如何正确解码前端传来的 json 对象(如 {"世":1,"界":1}),指出结构体绑定失败的根本原因,并提供直接解码为 map[string]int 的简洁方案,同时说明如何安全地将数据以 json 格式响应给前端。

你遇到的问题非常典型:前端调用 JSON.stringify({ "世": 1, "界": 1, "最": 1, "強": 1 }) 发送的是一个顶层 JSON 对象(object),而非包含该对象的 JSON 结构体数组或包装对象。而你的 Go 结构体:

type text struct {
    Text map[string]int
}

期望接收到的 JSON 形如:

{ "Text": { "世": 1, "界": 1, "最": 1, "強": 1 } }

但实际收到的是:

{ "世": 1, "界": 1, "最": 1, "強": 1 }

——二者 JSON 结构不匹配,导致 json.Decode(&t) 静默失败(字段未填充),最终 t.Text 为 nil,日志输出 map[]。

✅ 正确做法是:跳过结构体,直接解码为 map[string]int,因为它与原始 JSON 的顶层结构完全一致:

func PostHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("post start")
    if r.Method != "POST" {
        http.NotFound(w, r)
        return
    }

    decoder := json.NewDecoder(r.Body)
    var data map[string]int // 直接对应 {"key": value} 的 JSON object
    if err := decoder.Decode(&data); err != nil {
        log.Printf("JSON decode error: %v", err)
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    log.Printf("Decoded: %+v", data) // 输出 map[世:1 界:1 最:1 強:1]

    // ✅ 正确返回 JSON 给前端:使用 json.Marshal + 设置 Content-Type
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    if err := json.NewEncoder(w).Encode(data); err != nil {
        log.Printf("JSON encode error: %v", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
}

? 关键要点说明:

  • 不要强制套用结构体:当 JSON 是扁平对象且无固定字段名时(如动态键名的

    词频映射),map[string]T 是最自然、最安全的选择;
  • 避免 w.Write([]byte(t.Text)) 类型错误:map[string]int 不能直接转 []byte,必须经 json.Marshal 或 json.NewEncoder 序列化;
  • 务必设置 Content-Type:前端(如 fetch)依赖此头识别响应为 JSON,否则可能解析失败;
  • 错误处理不可省略:Decode 和 Encode 均可能失败,应检查 err 并返回恰当 HTTP 状态码(如 400 Bad Request / 500 Internal Server Error);
  • UTF-8 支持开箱即用:Go 的 encoding/json 默认支持 Unicode(包括中文键名),无需额外配置。

? 进阶提示:若后续需扩展为带元信息的结构(如添加 timestamp 或 version),可定义结构体并使用 json.RawMessage 延迟解析动态部分,但当前场景保持简单即最优解。