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

Delphi RIO中TIdHTTP发送GET请求遇403错误,Postman可正常访问

Delphi RIO中TIdHTTP发送GET请求返回403 Forbidden,但Postman可正常访问的排查

问题描述

在Delphi RIO环境下使用TIdHTTP组件发送GET请求时,收到HTTP 403 Forbidden错误,但相同URL在Postman中能正常访问。该函数在其他场景可正常工作,调整多种TIdHTTP配置后问题仍存在,需排查原因。

函数实现

function HTTPRestApiGet(var HTTP: TIdHTTP; necesitaSSL: Boolean; var SSL: TIdSSLIOHandlerSocketOpenSSL; Usuario, Clave, URLBase, Recurso, CamposSolicitados: string; TiempoEspera: Integer = 5000;
  LogActivado: Boolean = True): THTTPRestApiResponse;
var
  requerimiento, respuesta: string;
  LogFile                 : TIdLogFile;
begin
  LogFile := TIdLogFile.Create(nil);
  //
  HTTP.HandleRedirects := True;
  HTTP.RedirectMaximum := 10;
  HTTP.ReadTimeout := TiempoEspera;
  HTTP.MaxAuthRetries := 0;
  HTTP.HTTPOptions := [hoInProcessAuth];
  HTTP.Request.BasicAuthentication := True;
  HTTP.Request.Username := Usuario;
  HTTP.Request.Password := Clave;
  HTTP.Request.Accept := 'http';
  HTTP.Request.ContentType := 'application/json';
  //
  HTTP.IOHandler := nil;
  if necesitaSSL then
    begin
      HTTP.IOHandler := SSL;
      SSL.SSLOptions.Mode := sslmClient;
      SSL.SSLOptions.Method := sslvSSLv23;
    end;
  // Configurar logging
  LogFile.Filename := 'http_log.txt';
  LogFile.Active := True;
  HTTP.Intercept := LogFile;
  //
  try
    requerimiento := URLBase + IfThen(Recurso <> EmptyStr, '/' + Recurso + IfThen(CamposSolicitados <> EmptyStr, '?_fields=' + CamposSolicitados, ''), EmptyStr);
    try
      respuesta := HTTP.Get(requerimiento);
    except
    end;
  finally
    HTTPRestApiLogRespuesta('GET', requerimiento, respuesta, HTTP.URL.uri, HTTP.ResponseText, HTTP.ResponseCode, LogActivado);
    with Result do
      begin
        requerimiento_exitoso := (HTTP.ResponseCode >= 200) and (HTTP.ResponseCode < 300);
        respuesta_codigo := HTTP.ResponseCode;
        respuesta_codigotexto := HTTP.ResponseText;
        respuesta_texto := respuesta;
        respuesta_id := -1;
      end;
    //
    LogFile.Free;
  end;
end;

调用代码

HTTP := TIdHTTP.Create(nil);
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
respuesta := HTTPRestApiGet(HTTP, True, SSL, 'brgroup', '1234', 'https://apis.datos.gob.ar/series/api/series/?ids=148.3_INIVELNAL_DICI_M_26&start_date=' + fecha_desde + '&end_date=' + fecha_hasta,
  EmptyStr, EmptyStr, 5000, False);
SSL.Free;
HTTP.Free;

日志返回信息

HTTP/1.1 403 Forbidden

Stat Connected.
Sent 03/08/2024 14:31:22: GET /series/api/series/?ids=148.3_INIVELNAL_DICI_M_26&start_date=2024-04-01&end_date=2024-07-01 HTTP/1.1<EOL>Content-Type: application/json<EOL>Host: apis.datos.gob.ar<EOL>Accept: http<EOL>User-Agent: Mozilla/3.0 (compatible; Indy Library)<EOL>Authorization: Basic Og==<EOL><EOL>
Recv 03/08/2024 14:31:22: HTTP/1.1 403 Forbidden<EOL>Date: Sat, 03 Aug 2024 17:31:18 GMT<EOL>Content-Type: text/plain; charset=UTF-8<EOL>Content-Length: 16<EOL>Connection: keep-alive<EOL>X-Frame-Options: SAMEORIGIN<EOL>Referrer-Policy: same-origin<EOL>Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0<EOL>Expires: Thu, 01 Jan 1970 00:00:01 GMT<EOL>Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=R4%2FV8fU73LVHwpLIjJ6e%2Bq91jVM76aHSX9KolkAQ2jd6gUdAGWjUl5yOZd728jdQb%2BoIx%2BzuQyFnqhINrrA76CWjQv%2Fw17ciaSSFRAmKOMSEBszwUe3s6K0en7dhPVXEC3wH5A%3D%3D"}],"group":"cf-nel","max_age":604800}<EOL>NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}<EOL>Server: cloudflare<EOL>CF-RAY: 8ad80a629ecaa78f-EZE<EOL>alt-svc: h3=":443"; ma=86400<EOL><EOL>error code: 1010

Postman正常请求的curl命令

curl --location 'https://apis.datos.gob.ar/series/api/series/?ids=148.3_INIVELNAL_DICI_M_26&start_date=2024-04-01&end_date=2024-07-01' \
--header 'Accept: http' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic Og=='

排查分析与解决方案

核心问题定位

从日志可见请求被Cloudflare拦截(Server: cloudflare、error code: 1010),结合Postman与TIdHTTP的请求差异,主要问题点如下:

  1. User-Agent标识问题:TIdHTTP默认的User-Agent属于老旧且明确标识为程序库的请求头,Cloudflare反爬机制易将其判定为非合法请求。
  2. Authorization头异常:日志中认证头解码后为空,说明TIdHTTP自动生成Basic认证头的逻辑未正常工作,可能是MaxAuthRetries := 0与hoInProcessAuth的组合导致认证流程未触发。
  3. SSL/TLS协议版本兼容:当前设置的sslvSSLv23协议范围较广,Cloudflare可能强制要求TLS 1.2及以上版本,旧协议易被拦截。

针对性解决方案

1. 修改User-Agent为浏览器或Postman标识

在函数中添加或修改User-Agent设置:

HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';
// 或使用Postman的User-Agent
// HTTP.Request.UserAgent := 'PostmanRuntime/7.39.0';

2. 手动构造Authorization认证头

关闭TIdHTTP自动Basic认证,手动生成认证头避免逻辑异常:

HTTP.Request.BasicAuthentication := False;
HTTP.Request.CustomHeaders.Add('Authorization: Basic ' + TIdEncoderMIME.EncodeString(Usuario + ':' + Clave));

3. 指定SSL/TLS协议版本为TLS 1.2或更高

将SSL协议方法修改为TLS 1.2:

SSL.SSLOptions.Method := sslvTLSv1_2;
// 若环境支持,可尝试TLS 1.3
// SSL.SSLOptions.Method := sslvTLSv1_3;

4. 修正Accept请求头(可选)

虽然Postman也传了Accept: http,但该值不符合标准MIME类型规范,可修改为更通用的格式:

HTTP.Request.Accept := 'application/json, */*';

验证步骤

优先修改User-Agent并测试,若问题解决则说明是反爬拦截;若仍存在问题,再依次验证Authorization头和SSL协议版本的调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 03:49:50