Go语言中GET请求外部API时如何强制获取JSON响应?
解决Go请求API时压测出现JSON解析错误的问题
这个坑我之前踩过!压测时服务器扛不住返回了HTML错误页(比如500、503这类状态码对应的错误页面),你的代码直接把它当JSON解析,就会出现invalid character '<' looking for beginning of value这个错误。咱们来一步步修复:
核心问题分析
你代码里有两个关键错误:
- 错误设置响应头:
resp.Header.Set("Content-Type", contentType)是在修改服务器返回的响应头,这完全没用!服务器不会因为你改了响应头就返回JSON,你需要在发送请求时告诉服务器你只接受JSON格式。 - 没检查响应状态码:压测时服务器很可能因为过载返回非2xx的状态码(比如500),这时返回的是HTML错误页,你直接去解析JSON肯定失败。
修复后的代码示例
1. 重构Request函数,正确设置请求头并检查状态码
import ( "fmt" "io/ioutil" "log" "net/http" ) func Request(url string, acceptType string) ([]byte, error) { // 创建GET请求,而非直接用http.Get,这样可以自定义请求头 req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } // 设置Accept请求头,明确告诉服务器我们只接受JSON响应 req.Header.Set("Accept", acceptType) // 使用带超时的客户端,避免压测时请求挂起 client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("请求发送失败: %w", err) } defer resp.Body.Close() // 用defer确保响应体被关闭,避免资源泄漏 // 先检查响应状态码,非2xx直接返回错误,同时带上错误内容方便调试 if resp.StatusCode < 200 || resp.StatusCode >= 300 { errorBody, _ := ioutil.ReadAll(resp.Body) return nil, fmt.Errorf("意外状态码 %d: %s", resp.StatusCode, string(errorBody)) } // 读取响应体 body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("读取响应体失败: %w", err) } return body, nil }
2. 修改调用逻辑,优雅处理错误
url := fmt.Sprintf("https://example.com/api/category/%s", category) acceptType := "application/json" body, err := Request(url, acceptType) if err != nil { // 压测时不要直接Fatal退出,记录日志后继续处理其他请求 log.Printf("请求出错: %v", err) return // 或者根据业务逻辑做重试/跳过 } res := &JSONRespStruct{} err = json.Unmarshal(body, res) if err != nil { log.Printf("JSON解析失败: %v,响应内容: %s", err, string(body)) return }
额外优化建议
- 增加重试逻辑:对5xx类的服务器错误,可以做有限次数的重试,但要注意间隔时间,避免给服务器增加额外负担。
- 限制并发数:压测时如果并发太高,服务器更容易崩溃,你可以在
http.Client的Transport中设置最大并发连接数。 - 监控响应内容:压测时可以偶尔打印响应内容,确认服务器确实返回了符合预期的JSON格式数据。
内容的提问来源于stack exchange,提问作者Lanti
相关产品推荐
相关产品推荐

