使用HttpClient发送大JSON文件时抛出HttpRequestException问题排查
结合你遇到的现象——小体积JSON请求正常,800KB以上就抛出「服务器返回无效或无法识别的响应」异常,且Postman测试完全正常,问题大概率出在HttpClient处理大请求/响应的配置或传输方式上,以下是几个针对性的排查和解决方向:
1. 改用StreamContent替代StringContent发送请求
你的代码里用StringContent承载序列化后的JSON,当JSON体积较大时,会把整个字符串加载到内存中,不仅效率低,还可能触发某些底层的缓冲限制,导致请求发送不完整,服务器返回异常响应。换成StreamContent流式传输会更稳定:
public async Task<TResponse> MakeRequest<TRequest, TResponse>(TRequest request) { if (request == null) throw new ArgumentNullException(nameof(request)); // 序列化到内存流,避免占用过多内存 using var ms = new MemoryStream(); using var writer = new StreamWriter(ms, Encoding.UTF8); var serializer = JsonSerializer.CreateDefault(); serializer.Serialize(writer, request); await writer.FlushAsync(); ms.Position = 0; var content = new StreamContent(ms); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, Content = content }; // 先读取响应头,再流式读取响应内容,避免一次性缓冲大响应 var response = await _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); // 流式读取响应,避免大字符串加载内存 using var responseStream = await response.Content.ReadAsStreamAsync(); using var streamReader = new StreamReader(responseStream); using var jsonReader = new JsonTextReader(streamReader); var responseObject = JsonSerializer.CreateDefault().Deserialize<TResponse>(jsonReader); return responseObject ?? throw new InvalidOperationException(); }
2. 延长HttpClient的超时时间
大体积JSON的传输和处理耗时更长,默认HttpClient的超时时间是100秒,如果外部服务处理大请求超过这个时间,可能会导致连接中断,表现为「无效响应」的异常。你可以在注入HttpClient时延长超时:
services.AddHttpClient<IExternalWebServiceProxy, ExternalWebServiceProxy>() .ConfigureHttpClient(x => { x.MaxResponseContentBufferSize = 1024 * 1000 * 100; // 100MB x.Timeout = TimeSpan.FromMinutes(5); // 根据实际情况调整超时时间 });
3. 明确指定请求内容的编码
你当前创建StringContent时没有指定编码,默认会用UTF-16,但大多数REST服务期望的是UTF-8编码的JSON。编码不匹配可能导致服务器解析请求内容失败,返回异常响应。如果坚持用StringContent,记得明确指定编码:
var jsonString = JsonConvert.SerializeObject(request); var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
4. 验证服务器Chunked传输编码的兼容性
有些服务器在处理大响应时会启用Chunked传输编码,而ASP.NET Core 2.2的HttpClient在某些场景下对Chunked响应的解析可能存在兼容性问题。你可以尝试在请求头中明确允许Chunked传输:
httpRequestMessage.Headers.TransferEncodingChunked = true;
(前面的流式读取方式已经能很好处理Chunked编码的响应,这个配置可以作为补充尝试)
5. 检查请求Content-Length的正确性
当用StringContent时,它会自动计算Content-Length,但如果序列化后的字符串包含特殊字符或编码转换问题,可能导致Content-Length计算错误,服务器收到的内容不完整,从而返回无效响应。改用StreamContent后,HttpClient会自动处理Content-Length或启用Chunked,能避免这个问题。
内容的提问来源于stack exchange,提问作者Iskander Raimbaev

