【问题标题】:TIdHTTP character encoding of POST error responsePOST 错误响应的 TIdHTTP 字符编码
【发布时间】:2018-05-23 17:34:01
【问题描述】:

我正在使用带有 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

【问题讨论】:

    标签: delphi post decoding indy10 idhttp


    【解决方案1】:

    由于您使用的是 Delphi 7,原生的 string 类型是 AnsiString,这很重要,因为这意味着 Indy 在解码字符串时需要做更多的工作。

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

    在 Delphi/FreePascal 的 Unicode 前版本中,然后将 Unicode 数据转换为 ANSI 以适应 AnsiString。在那些编译器版本中,返回stringTIdHTTP 方法有一个可选的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 前版本)。

    您有几个选项可以解决此问题:

    1. 在调用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;
      
    2. 由于您将成功和失败响应都保存到同一个 responseBody 变量中,您不妨完全禁用 EIdHTTPProtocolException,并删除您的 try/except 块。您可以通过在调用Post() 之前启用TIdHTTP.HTTPOptions 属性中的hoNoProtocolErrorExceptionhoWantProtocolErrorContent 标志来做到这一点。您可以检查TIdHTTP.ResponseCode 属性来区分成功和失败响应:

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

    【讨论】:

    • 我使用了第一个选项,它奏效了。非常感谢,从你的回答中学到了很多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    相关资源
    最近更新 更多