У меня проблема.
Я пробую различные возможные решения, но что-то в моем коде все еще не так, и я не могу понять, что именно.
Функция:
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 Запрещено
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 --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=='
Кто-нибудь может мне с этим помочь?
Пробую разные TIdHTTP
варианты, но ничего не получается.
Для других целей функция работает нормально.
Вы не показали необработанные данные запроса, которые отправляет Curl, но единственная разница в последствиях, которую я вижу между вашим кодом TIdHTTP
и вашей командой Curl, — это заголовок запроса User-Agent
.
Многие серверы чувствительны к запрашивающему агенту и отправляют разные данные разным агентам. Такие серверы нередко отклоняют/не распознают значение Indy по умолчанию User-Agent
.
Когда я тестировал ваш код, он действительно потерпел неудачу со значением User-Agent
по умолчанию в Indy, равным Mozilla/3.0 (compatible; Indy Library)
, и когда вместо этого я установил для свойства TIdHTTP.Request.UserAgent
(или глобальной переменной GIdDefaultUserAgent
) значение, имитирующее реальный веб-браузер (в моем случае Firefox), запрос сработал должным образом, и был возвращен ответ JSON:
HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0';
Учитывая популярность Curl, я не удивлюсь, если серверы, поддерживающие агенты, распознают Curl. К вашему сведению, значение User-Agent
по умолчанию для Curl равно curl/[version]
(например: curl/8.9.1
, что также работало в моем TIdHTTP
тесте).
Когда вы сравниваете запросы Curl с другими HTTP-библиотеками, вы всегда должны стремиться к тому, чтобы запросы были как можно более идентичными, чтобы исключить любые возможные различия, которые могут сбить с толку серверы. А когда между похожими запросами существуют различия в поведении, обычно виноват User-Agent
.
В качестве примечания:
Нет смысла отправлять заголовок Content-Type
в запросе GET
, поскольку в теле HTTP-запроса данные не отправляются. С другой стороны, если вы хотите сообщить серверу, что принимаете в ответ только JSON, вместо этого отправьте заголовок запроса Accept: application/json
.
'http'
не является допустимым типом носителя для заголовка запроса Accept
.
Установка для свойства TIdHTTP.SSLOptions.Method
значения sslvSSLv23
включит SSL v2.0–v3.0 в дополнение к TLS v1.0–1.2. Не делай этого! Вместо этого используйте это:
HTTP.SSLOptions.SSLVersions := [sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2];
Вы не отправляете никаких учетных данных для аутентификации. В заголовке Authorization
вашего запроса в обоих случаях:
Authorization: Basic Og==
Og==
— это кодировка base64 для одного символа ':'
, который разделяет имя пользователя и пароль. Это означало бы, что ваши переменные Usuario
и Clave
представляют собой пустые строки, но на самом деле это не так в показанном вами коде TIdHTTP
, поэтому заголовок Authorization
, который генерирует TIdHTTP
, должен был выглядеть следующим образом, учитывая Usuario=brgroup
и Clave=1234
:
Authorization: Basic YnJncm91cDoxMjM0
Но, учитывая, что рассматриваемый сервер на самом деле не требует аутентификации, нет никакой причины отправлять заголовок Authorization
вообще. Я пропустил это в своем тесте TIdHTTP
и все равно смог получить ответ в формате JSON.
Отличный ответ!!! Спасибо вам!!! оно работает!