由于您使用的是 Delphi 7,原生的 string 类型是 AnsiString,这很重要,因为这意味着 Indy 在解码字符串时需要做更多的工作。
TIdHTTP 将 HTTP 响应正文解析为 string 时,首先使用响应字符集将其解码为 Unicode,如服务器报告的那样。例如,如果服务器以 UTF-8 编码发送响应,则需要在 Content-Type 标头的 charset 属性中指定。
在 Delphi/FreePascal 的 Unicode 前版本中,然后将 Unicode 数据转换为 ANSI 以适应 AnsiString。在那些编译器版本中,返回string 的TIdHTTP 方法有一个可选的ADestEncoding 参数,可让您指定要将Unicode 数据转换为哪种AnsiString 编码。如果不指定,则使用 Indy 的默认编码,默认为 US-ASCII(参见IdGlobal 单元中的全局GIdDefaultTextEncoding 变量)。
你真的应该让 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;
如果您曾经升级到 Unicode 版本的 Delphi,则可以简单地删除额外的 UTF-8 步骤:
try
responseBody := IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody);
except
on E: EIdHTTPProtocolException do
responseBody := E.ErrorMessage;
end;
在您在问题中提供的示例中,您绕过了 TIdHTTP 的自动解码逻辑,将原始响应正文 按原样 接收到 TStream 而不是 string .在这种情况下,您有责任确保检查响应的charset 以了解如何正确解码原始数据。它可能并不总是 UTF-8。 Indy 具有 ReadStringFromStream() 和 ReadStringAsCharset() 函数,允许您在从 TStream 读取 string 时指定编码/字符集。
现在,回答您的问题,您为什么不能正确解码EIdHTTPProtocolException.ErrorMessage?好吧,因为它已经被TIdHTTP 为你解码了。
然而,问题来了 - 当解码错误响应以放入 EIdHTTPProtocolException 时,ADestEncoding 参数当前无法从引发异常的代码中访问,因此 Indy 的默认值encoding 被使用,默认情况下是 US-ASCII。这就是为什么您会看到“特殊”字符被转换为 ? 的原因(同样,这只会影响 Delphi/FreePascal 的 Unicode 前版本)。
您有几个选项可以解决此问题:
-
在调用Post() 之前,将全局IdGlobal.GIdDefaultTextEncoding 变量设置为encUTF8。这样,如果引发EIdHTTPProtocolException,则其ErrorMessage 将被UTF-8 编码。请注意,这确实会在全球范围内影响 Indy,并且在 Unicode 之前的 Delphi 版本中比在 Unicode 版本中的影响要大得多,所以要小心。
GIdDefaultTextEncoding := encUTF8;
...
try
...
responseBody := ...;
except
on E: EIdHTTPProtocolException do
responseBody := UTF8Decode(E.ErrorMessage);
end;
-
由于您将成功和失败响应都保存到同一个 responseBody 变量中,您不妨完全禁用 EIdHTTPProtocolException,并删除您的 try/except 块。您可以通过在调用Post() 之前启用TIdHTTP.HTTPOptions 属性中的hoNoProtocolErrorException 和hoWantProtocolErrorContent 标志来做到这一点。您可以检查TIdHTTP.ResponseCode 属性来区分成功和失败响应:
IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
responseBody := UTF8Decode(
IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody,
IndyTextEncoding_UTF8)
);