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

使用Golang向NFT.Storage发送POST请求返回500错误问题求助

问题根因

你的代码构造的multipart/form-data请求完全不符合协议规范,和能正常运行的cURL请求结构存在本质差异:

  • 标准multipart请求必须携带随机生成的boundary分隔符:该分隔符需要同时在Content-Type请求头中声明、在请求体中用于分割不同表单字段。你手动硬编码Content-Type: multipart/form-data时没有携带boundary参数,服务端根本无法正确解析请求体内容。
  • 你使用url.Values.Encode()生成的是application/x-www-form-urlencoded格式的请求体(URL编码的键值对格式),哪怕去掉URL编码直接拼接JSON字符串,也不符合multipart要求的分段结构。
  • 能正常运行的cURL命令使用-F参数时,会自动生成合法boundary、拼接符合规范的分段请求体、自动写入带正确boundary的Content-Type头,这是两者表现不一致的核心原因。

额外注意:你的Payload结构体字段必须首字母大写(对外可导出),否则json.Marshal序列化时会忽略未导出字段,导致meta字段内容不符合接口要求。

修复方案

Go标准库提供了mime/multipart包专门用于构造合法的multipart/form-data请求,不要手动拼接请求体、不要手动硬写Content-Type头,参考以下正确实现:

import (
	"bytes"
	"encoding/json"
	"mime/multipart"
	"net/http"
)

func (h *Handler) uploadNftImage(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	// 序列化meta字段内容
	data := Payload{Name: "Hello", Image: nil}
	payloadBytes, err := json.Marshal(data)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 初始化multipart写入器,会自动生成合法的随机boundary
	var requestBody bytes.Buffer
	multipartWriter := multipart.NewWriter(&requestBody)

	// 添加meta表单字段
	fieldWriter, err := multipartWriter.CreateFormField("meta")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	_, err = fieldWriter.Write(payloadBytes)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 必须关闭写入器,才会在请求体末尾写入结束分隔符
	err = multipartWriter.Close()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 构造POST请求
	req, err := http.NewRequest("POST", "https://api.nft.storage/store", &requestBody)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 设置请求头,Content-Type直接使用写入器生成的值,自带匹配的boundary参数
	req.Header.Set("accept", "application/json")
	req.Header.Set("Authorization", "Bearer MYPRETTYTOKEN")
	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())

	// 发起请求
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	// 可按需读取resp.Body做后续业务处理
	w.WriteHeader(resp.StatusCode)
}
通用排查思路
  • 遇到表单类请求报错时,第一时间抓包对比正常请求和异常请求的原始报文:你抓到的异常请求中既没有boundary声明,请求体也没有分段标记,很容易定位到格式不符合规范的问题。
  • 所有multipart请求场景都不要手动拼接请求体,各语言的标准HTTP客户端都自带成熟的multipart构造能力,手动拼接极易出现分隔符缺失、boundary与请求头不匹配的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:01:10