
本文详解如何在 Go Web 服务器中接收并解析 iOS 应用通过 NSData 序列化后 POST 的 JSON 数据,包括结构体定义、请求体读取、JSON 反序列化及 receipt 字段的 Base64 编码处理。
本文详解如何在 go web 服务器中接收并解析 ios 应用通过 nsserialization.datawithjsonobject 序列化后 post 的 json 数据,包括结构体定义、请求体读取、json 反序列化及 receipt 字段的 base64 编码处理。
在 iOS 端,你使用 NSJSONSerialization.dataWithJSONObject(...) 将包含 "receipt"(原始二进制数据)和 "prod_id"(字符串)的字典序列化为 UTF-8 编码的 JSON 字节流,并通过 HTTPBody 发送到 Go 服务器。此时,服务端接收到的是标准 JSON 格式 HTTP 请求体(Content-Type 通常为 application/json),需按以下步骤安全、规范地解析:
1. 定义匹配的 Go 结构体
iOS 发送的 JSON 示例:
{
"receipt": "MII...base64-encoded-bytes...",
"prod_id": "com.example.product"
}
注意:iOS 实际发送的 receipt 是原始二进制数据(NSData),但 NSJSONSerialization 会自动将其转换为 Base64 字符串(这是 JSON 规范要求——二进制无法直接嵌入 JSON,必须编码)。因此,Go 端应将 receipt 字段定义为 string 类型,并在后续按需解码。
用于端到端视频本地化流程的轻量编排器,路由至四个专注子技能——/wjs-transcribing-audio、/wjs-translating-subtitles...
type ReceiptRequest struct {
Receipt string `json:"receipt"` // iOS 自动 Base64 编码后的字符串
ProdID string `json:"prod_id"` // 产品标识符
}
2. 读取并解析请求体
务必检查 Content-Type、限制请求体大小(防 DoS)、及时关闭 Body,并处理错误:
func handleReceipt(w http.ResponseWriter, r *http.Request) {
// 验证请求方法与 Content-Type
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Content-Type") != "application/json" {
http.Error(w, "Invalid Content-Type, expected application/json", http.StatusBadRequest)
return
}
// 限制请求体大小(例如最大 1MB)
r.Body = http.MaxBytesReader(w, r.Body, 1024*1024)
// 读取全部请求体
data, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
defer r.Body.Close() // 注意:io.ReadAll 后仍建议显式 Close
// 解析 JSON 到结构体
var req ReceiptRequest
if err := json.Unmarshal(data, &req); err != nil {
http.Error(w, "Invalid JSON format: "+err.Error(), http.StatusBadRequest)
return
}
// 此时 req.Receipt 是 Base64 字符串,req.ProdID 是纯字符串
fmt.Printf("Product ID: %s\n", req.ProdID)
fmt.Printf("Receipt (Base64): %s\n", req.Receipt)
// 如需还原原始 receipt 二进制数据(例如验证 Apple receipt):
receiptBytes, err := base64.StdEncoding.DecodeString(req.Receipt)
if err != nil {
http.Error(w, "Invalid Base64 in receipt field", http.StatusBadRequest)
return
}
fmt.Printf("Receipt length (bytes): %d\n", len(receiptBytes))
// ✅ 后续可对 receiptBytes 进行 Apple IAP 验证等操作
}
3. 关键注意事项
- 不要手动对 receipt 再次 Base64 编码:iOS 已完成编码,Go 直接使用 req.Receipt 即可;若需二进制,调用 base64.StdEncoding.DecodeString() 还原。
- 始终校验 Content-Type 和 JSON 结构:避免因格式错误导致 panic 或逻辑漏洞。
- 设置 MaxBytesReader:防止恶意大请求耗尽内存。
- io.ReadAll 后仍需 defer r.Body.Close():虽 ReadAll 会消耗 Body,但显式关闭是 Go 最佳实践(尤其在中间件链中)。
- 字段标签 json:"..." 必须与 iOS 键名完全一致(区分大小写):如 "prod_id" 对应 ProdID string json:"prod_id",而非 ProdId 或 ProductId。
通过以上步骤,你的 Go 服务即可健壮、高效地接收并处理 iOS 发送的结构化 receipt 数据,为后续 App Store 验证提供可靠基础。










