POST 错误响应的 TIdHTTP 字符编码
TIdHTTP character encoding of POST error response
我正在使用 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
?
由于您使用的是 Delphi 7,因此本机 string
类型是 AnsiString
,这一点很重要,因为这意味着 Indy 在解码字符串时必须做更多的工作。
当 TIdHTTP
将 HTTP 响应 body 解析为 string
时,首先使用服务器报告的响应字符集将其解码为 Unicode。例如,如果服务器以 UTF-8 编码发送响应,则需要在 Content-Type
header.
的 charset
属性中指定
在 Delphi/FreePascal 的 pre-Unicode 版本中,Unicode 数据随后被转换为 ANSI 以适应 AnsiString
。在这些编译器版本中,return 和 string
的 TIdHTTP
方法有一个可选的 ADestEncoding
参数,让您指定要将 Unicode 数据转换为哪种 AnsiString
编码.如果不指定,则使用Indy的默认编码,默认为US-ASCII(参见IdGlobal
单元中的全局GIdDefaultTextEncoding
变量)。
你真的应该让 Indy 为你处理这个解码,因为不能保证任何给定的响应都是 UTF-8 编码的。但是,您可以指定您希望 Indy 的输出始终采用 UTF-8 编码(仅限 pre-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
的自动解码逻辑,接收原始响应 body as-is 变成 TStream
而不是 string
。在这种情况下,您有责任确保检查响应的 charset
以了解如何正确解码原始数据。它可能并不总是 UTF-8。 Indy 具有 ReadStringFromStream()
和 ReadStringAsCharset()
函数,允许您在从 TStream
.
读取 string
时指定 encoding/charset
现在,回答你的问题,为什么你不能正确解码 EIdHTTPProtocolException.ErrorMessage
?嗯,因为 已经 被 TIdHTTP
为你解码了。
HOWEVER,这里是难点 - 在解码错误响应以放入 EIdHTTPProtocolException
时,ADestEncoding
参数当前无法从代码访问这会引发异常,因此会使用 Indy 的默认编码,默认情况下为 US-ASCII。这就是为什么您看到 "special" 个字符被转换为 ?
(同样,这只影响 Delphi/FreePascal 的 pre-Unicode 版本)。
您有几个选项可以解决此问题:
在调用 Post()
之前将全局 IdGlobal.GIdDefaultTextEncoding
变量设置为 encUTF8
。这样,如果 EIdHTTPProtocolException
被引发,它的 ErrorMessage
将被 UTF-8 编码。请注意,这确实会在全球范围内影响 Indy,并且在 Delphi 的 pre-Unicode 版本中比在 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)
);
我正在使用 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
?
由于您使用的是 Delphi 7,因此本机 string
类型是 AnsiString
,这一点很重要,因为这意味着 Indy 在解码字符串时必须做更多的工作。
当 TIdHTTP
将 HTTP 响应 body 解析为 string
时,首先使用服务器报告的响应字符集将其解码为 Unicode。例如,如果服务器以 UTF-8 编码发送响应,则需要在 Content-Type
header.
charset
属性中指定
在 Delphi/FreePascal 的 pre-Unicode 版本中,Unicode 数据随后被转换为 ANSI 以适应 AnsiString
。在这些编译器版本中,return 和 string
的 TIdHTTP
方法有一个可选的 ADestEncoding
参数,让您指定要将 Unicode 数据转换为哪种 AnsiString
编码.如果不指定,则使用Indy的默认编码,默认为US-ASCII(参见IdGlobal
单元中的全局GIdDefaultTextEncoding
变量)。
你真的应该让 Indy 为你处理这个解码,因为不能保证任何给定的响应都是 UTF-8 编码的。但是,您可以指定您希望 Indy 的输出始终采用 UTF-8 编码(仅限 pre-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
的自动解码逻辑,接收原始响应 body as-is 变成 TStream
而不是 string
。在这种情况下,您有责任确保检查响应的 charset
以了解如何正确解码原始数据。它可能并不总是 UTF-8。 Indy 具有 ReadStringFromStream()
和 ReadStringAsCharset()
函数,允许您在从 TStream
.
string
时指定 encoding/charset
现在,回答你的问题,为什么你不能正确解码 EIdHTTPProtocolException.ErrorMessage
?嗯,因为 已经 被 TIdHTTP
为你解码了。
HOWEVER,这里是难点 - 在解码错误响应以放入 EIdHTTPProtocolException
时,ADestEncoding
参数当前无法从代码访问这会引发异常,因此会使用 Indy 的默认编码,默认情况下为 US-ASCII。这就是为什么您看到 "special" 个字符被转换为 ?
(同样,这只影响 Delphi/FreePascal 的 pre-Unicode 版本)。
您有几个选项可以解决此问题:
在调用
Post()
之前将全局IdGlobal.GIdDefaultTextEncoding
变量设置为encUTF8
。这样,如果EIdHTTPProtocolException
被引发,它的ErrorMessage
将被 UTF-8 编码。请注意,这确实会在全球范围内影响 Indy,并且在 Delphi 的 pre-Unicode 版本中比在 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) );