Ошибка 403 при использовании TIdHTTP для запроса GET в Delphi RIO. Тот же URL-адрес работает в Postman

У меня проблема.

Я пробую различные возможные решения, но что-то в моем коде все еще не так, и я не могу понять, что именно.

Функция:

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 варианты, но ничего не получается. Для других целей функция работает нормально.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не показали необработанные данные запроса, которые отправляет 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.

Отличный ответ!!! Спасибо вам!!! оно работает!

Speaker 04.08.2024 17:08

Другие вопросы по теме