Кодировка символов TIdHTTP ответа на ошибку POST - PullRequest
0 голосов
/ 23 мая 2018

Я использую Delphi 7 с Indy 10.6.2.5459, чтобы сделать POST запрос к серверу.Все работает нормально, за исключением кодировки ответа, когда происходит EIdHTTPProtocolException.

Когда я не поднимаю EIdHTTPProtocolException, я могу декодировать ответ следующим образом, чтобы правильно получить специальные символы:

responseBody := '';
responseContent := TStringStream.Create('');
try
  try
    IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody, responseContent);
    responseBody := UTF8Decode(responseContent.DataString);
  except
    on E: EIdHTTPProtocolException do
      responseBody := UTF8Decode(E.ErrorMessage);
  end;
finally
  FreeAndNil(responseContent);
end;

Но, когда EIdHTTPProtocolException поднято, свойство E.ErrorMessage имеет ? вместо ожидаемых специальных символов, даже при использовании UTF8Decode().

Итак, как я могуправильно декодировать E.ErrorMessage?

1 Ответ

0 голосов
/ 24 мая 2018

Поскольку вы используете Delphi 7, собственный тип string равен AnsiString, что важно отметить, поскольку это означает, что Indy приходится выполнять больше работы при декодировании строк.

Когда TIdHTTP анализируеттело ответа HTTP в string, оно сначала декодируется в Unicode с использованием кодировки ответа, как сообщает сервер.Например, если сервер отправляет ответ в кодировке UTF-8, его необходимо указать в атрибуте charset заголовка Content-Type.

В версиях Delphi / FreePascal, предшествующих Unicode,эти данные Unicode затем преобразуются в ANSI, чтобы поместиться в AnsiString.В этих версиях компилятора методы TIdHTTP, которые возвращают string, имеют необязательный параметр ADestEncoding, позволяющий указать, в какую кодировку AnsiString требуется преобразовать данные Unicode.Если не указано иное, используется кодировка Indy по умолчанию, которая по умолчанию является US-ASCII (см. Глобальную переменную GIdDefaultTextEncoding в блоке IdGlobal).

Вы действительно должны позволить Indy обработать это декодирование для вас,поскольку нет никакой гарантии, что любой данный ответ должен быть в кодировке UTF-8.Однако вы можете указать, что вы хотите, чтобы выход Indy всегда был в кодировке UTF-8 (только для версий, предшествующих Unicode), например:

try
  responseBody := UTF8Decode(
    IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody,
      IndyTextEncoding_UTF8)
  );
except
  on E: EIdHTTPProtocolException do
    responseBody := E.ErrorMessage;
end;

Если вы когда-либо обновитесь до версии Delphi с Unicode, вы можетезатем просто удалите лишний шаг UTF-8:

try
  responseBody := IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody);
except
  on E: EIdHTTPProtocolException do
    responseBody := E.ErrorMessage;
end;

В примере, который вы предоставили в своем вопросе, вы обходите логику автоматического декодирования TIdHTTP, получая необработанное тело ответа as- в TStream вместо string.В этом случае вы несете ответственность за проверку charset ответа, чтобы знать, как правильно декодировать необработанные данные.Это не всегда может быть UTF-8.Indy имеет функции ReadStringFromStream() и ReadStringAsCharset(), которые позволяют указывать кодировку / кодировку при чтении string из TStream.

Теперь, чтобы ответить на ваш вопрос, почему вы не можете декодироватьEIdHTTPProtocolException.ErrorMessage правильно?Ну, поскольку он уже был декодирован для вас TIdHTTP.

HOWEVER , вот в чем проблема - при декодировании ответа об ошибке для помещения в EIdHTTPProtocolException, параметр ADestEncoding в настоящее время НЕ доступен из кода, который вызывает исключение, поэтому вместо него используется стандартная кодировка Indy, которая по умолчанию является US-ASCII.Вот почему вы видите, что «специальные» символы преобразуются в ? (опять же, это влияет только на версии Delphi / FreePascal, предшествующие Unicode).

У вас есть несколько вариантов решения этой проблемы:

  1. установите глобальную переменную IdGlobal.GIdDefaultTextEncoding на encUTF8 перед вызовом Post().Таким образом, если EIdHTTPProtocolException поднят, его ErrorMessage будет закодирован в UTF-8.Обратите внимание, что это влияет на Indy глобально и оказывает гораздо большее влияние в версиях Delphi до Unicode, чем в версиях Unicode, поэтому будьте осторожны с ним.

    GIdDefaultTextEncoding := encUTF8;        
    ...
    try
      ...
      responseBody := ...;
    except
      on E: EIdHTTPProtocolException do
        responseBody := UTF8Decode(E.ErrorMessage);
    end;
    
  2. , так как вы сохраняетекак успешные, так и неудачные ответы на одну и ту же переменную responseBody, вы можете просто полностью отключить использование EIdHTTPProtocolException и удалить свой блок try/except.Это можно сделать, включив флаги hoNoProtocolErrorException и hoWantProtocolErrorContent в свойстве TIdHTTP.HTTPOptions перед вызовом Post().Вы можете проверить свойство TIdHTTP.ResponseCode, чтобы различать успешные и неудачные ответы:

    IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
    responseBody := UTF8Decode(
      IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody,
        IndyTextEncoding_UTF8)
    );
    
...