POST错误响应的TIdHTTP字符编码

时间:2018-05-23 17:34:01

标签: delphi post decoding indy10 idhttp

我正在使用带有Indy 10.6.2.5459的Delphi 7向服务器发出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 :(得分:4)

由于您使用的是Delphi 7,因此本地string类型为AnsiString,这一点非常重要,因为这意味着Indy在解码字符串时必须做更多工作。

TIdHTTP将HTTP响应主体解析为string时,它首先使用响应字符集解码为Unicode,如服务器报告的那样。例如,如果服务器以UTF-8编码发送响应,则需要在charset标头的Content-Type属性中指定该响应。

在Delphi / FreePascal的Unicode前版本中,然后将Unicode数据转换为ANSI以适合AnsiString。在这些编译器版本中,返回TIdHTTP的{​​{1}}方法具有可选的string参数,以便您指定要转换为Unicode数据的ADestEncoding编码。如果未指定,则使用Indy的默认编码,默认为US-ASCII(请参阅AnsiString单元中的全局GIdDefaultTextEncoding变量)。

你真的应该让Indy为你处理这个解码,因为不能保证任何给定的响应都是UTF-8编码的。但是,您可以指定您希望Indy的输出始终为UTF-8编码(仅限Unicode前版本),例如:

IdGlobal

如果您曾升级到Unicode版本的Delphi,那么您只需删除额外的UTF-8步骤:

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

在您的问题中提供的示例中,您绕过try responseBody := IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody); except on E: EIdHTTPProtocolException do responseBody := E.ErrorMessage; end; 的自动解码逻辑,将原始响应正文原样接收到TIdHTTP而不是一个TStream。在这种情况下,您负责确保检查响应string以了解如何正确解码原始数据。它可能不总是UTF-8。 Indy具有charsetReadStringFromStream()功能,可让您在从ReadStringAsCharset()阅读string时指定编码/字符集。

现在,为了回答您的问题,为什么不能正确解码TStream?好吧,因为已经已经由EIdHTTPProtocolException.ErrorMessage解码了

HOWEVER ,这就是问题 - 当解码错误响应以放入TIdHTTP时,EIdHTTPProtocolException参数当前不能从引发异常的代码中访问,因此使用Indy的默认编码,默认情况下是US-ASCII。这就是为什么你看到“特殊”字符转换为ADestEncoding的原因(同样,这只会影响Delphi / FreePascal的Unicode前版本)。

您可以通过以下几种方法解决此问题:

  1. 在调用?之前将全局IdGlobal.GIdDefaultTextEncoding变量设置为encUTF8。这样,如果引发Post(),则其EIdHTTPProtocolException将采用UTF-8编码。请注意,这确实会影响全局的Indy,并且在Unicode前版本的Delphi中比在Unicode版本中影响更大,因此请小心。

    ErrorMessage
  2. 由于您要将成功和失败响应保存到同一个GIdDefaultTextEncoding := encUTF8; ... try ... responseBody := ...; except on E: EIdHTTPProtocolException do responseBody := UTF8Decode(E.ErrorMessage); end; 变量,因此您也可以完全禁用responseBody,并删除EIdHTTPProtocolException块。您可以在调用try/except之前启用hoNoProtocolErrorException属性中的hoWantProtocolErrorContentTIdHTTP.HTTPOptions标记来执行此操作。您可以检查Post()属性以区分成功和失败响应:

    TIdHTTP.ResponseCode