第三方API前2次请求正常,第3次始终返回400错误求助
第三方API对接异常问题排查求助
核心现象
- 我方基于.NET 6构建的API,调用第三方Apache端点获取金融数据:单次请求第三方正常;单次我方API请求内,最多成功调用对方2次,第3次必失败
- 本地IIS开发环境、Azure部署环境均复现该问题
- 多层级集成测试(单独调用对方服务、编排多调用逻辑、业务流程外部端点)全部正常,仅通过我方API调用时出错
- 所有XML载荷经对方内部工具/Postman验证有效,调整请求顺序后仍是第3次失败;使用对方批量请求接口时,2次请求中的第2次也返回相同400错误
问题演进
- 初始使用远程DTD验证XML:第一次获取DTD+请求数据正常,第二次获取DTD返回400错误
- 将DTD本地化为嵌入式资源后:前2次数据请求成功,后续请求全失败(业务流程需要4次请求)
已尝试的无效方案
- 设置最大连接数超过2(含HttpClient处理器配置、web.config配置)
- 使用新实例化后立即释放的HttpClient(处理器最大连接数设为100)
- 修改DI作用域/生命周期
- 跳过应用层直接从控制器调用对方服务
关键异常线索
- Azure App Service HTTP追踪显示:即使URL指定HTTPS/443端口,底层HttpClient仍强制使用80端口,Flurl日志无重定向记录
- 对方服务器始终返回Apache的HTML格式400错误
新发现
- 流程作为Azure Durable Function直接运行、或通过NUnit集成测试运行时,对接对方服务完全正常
- 通过Function端点调用时,仅首次请求对方服务成功,后续请求均失败
核心调用方法
public static async Task<byte[]> PostToABCServiceAsync(this IRestHelper restHelper, RestRequest request, string xml, CancellationToken cancellationToken = default) { var content = new CapturedXmlContent(xml, "text/xml"); return await ((ABCRestHelper)restHelper).ExecuteAsync(request, (flrl) => flrl .WithHeader("Content-Type", "text/xml") .PostAsync(content, cancellationToken) .ReceiveBytes()); }
REST库入口方法
public async Task<HttpResponseMessage> PostAsync(RestRequest request, CancellationToken cancellationToken = default) { var content = request.Body as HttpContent; if (content == null && request.Body != null) { throw new ArgumentException("The request Body must inherit from System.Net.Http.HttpContent."); } var response = await ExecuteAsync(request, (flrl) => flrl.PostAsync(content, cancellationToken)); return response.ResponseMessage; }
REST库执行方法
public async Task<TResponse> ExecuteAsync<TResponse>(RestRequest request, Func<IFlurlRequest, Task<TResponse>> flurlRequest) { //build the URL first so we can render it how we want as far as encoding goes var requestUrl = request.BaseUri.AbsoluteUri .AppendPathSegments(request.Segments); if (request.QueryParameters?.Any() ?? false) { requestUrl = requestUrl.SetQueryParams(request.QueryParameters); } if (request.Port.HasValue) { requestUrl.Port = request.Port; } using (var client = new FlurlClient(requestUrl.ToString(request.EncodeSpaceAsPlus))) { if (request.Settings != null) { client.Configure(request.Settings); } if (request.BeforeCallAsync != null) { client.Settings.BeforeCallAsync = request.BeforeCallAsync; } if (request.AfterCallAsync != null) { client.Settings.AfterCallAsync = request.AfterCallAsync; } if (request.OnRedirectAsync != null) { client.Settings.OnRedirectAsync = request.OnRedirectAsync; } if (request.OnErrorAsync != null) { client.Settings.OnErrorAsync = request.OnErrorAsync; } client.AllowHttpStatus(string.Join(",", request.AllowHttpStatuses)); client.HttpClient.DefaultRequestHeaders.Clear(); client.Headers.Clear(); if (request.Headers?.Any() ?? false) { client.WithHeaders(request.Headers); } if (request.Timeout > 0) { client.WithTimeout(request.Timeout); } try { if (_throttle != null) { return await _throttle.Enqueue(() => flurlRequest(client.Request())); } else { return await flurlRequest(client.Request()); } } catch (FlurlHttpTimeoutException e) { throw new CustomRestTimeoutException(e.Call?.Request?.Url?.ToString(), e); } catch (FlurlParsingException e) { string responseContent = null; if (e.Call?.Response != null) { try { responseContent = await e.Call?.Response?.GetStringAsync(); } catch (Exception) { //eat all errors for pulling the response content } } throw new CustomRestParsingException(e.Call?.Request?.Url?.ToString(), responseContent, e); } catch (FlurlHttpException e) { int? statusCode = null; string responseErrorMessage = (e.Call?.Exception?.Message) ?? e.Message; string responseContent = null; if (e.Call?.HttpResponseMessage != null) { //if the call fails, the response might be null statusCode = (int?)e.Call.HttpResponseMessage.StatusCode; try { responseContent = await e.Call.HttpResponseMessage.Content.ReadAsStringAsync(); } catch (Exception) { //eat all errors for pulling the response content } } throw new CustomRestException(statusCode.HasValue ? (int)statusCode.Value : 0, e.Call?.Request?.Url?.ToString(), responseErrorMessage, responseContent, e); } } }
请求构建示例
CapturedXmlContent content = null; try { content = new CapturedXmlContent(_serializer.Serialize(query, false), "text/xml"); } catch (Exception e) { //log writing omitted throw; } var request = _requestFactory.GetRequest() .WithHeader("Content-Type", "text/xml") .WithBody(content) .OnBeforeCallAsync(LogBeforeCallAsync) .OnAfterCallAsync(LogAfterCallAsync) .OnRedirectAsync(LogRedirectAsync) .OnErrorAsync(LogErrorAsync) .Build(); try { var start = DateTime.UtcNow; using var response = await _restHelper.PostAsync(request, cancellationToken); var end = DateTime.UtcNow; return await ProcessZipResponseAsync<DataRequestDto, DataResponseDto>(query, response, attempt, start, end); } catch (Exception e) { //log writing omitted throw; }
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

