POST
我正在使用带有 Indy 10.6.2.5459 的 Delphi 7向服务器发出请求。一切正常,除了发生时的响应编码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;
但是,当 anEIdHTTPProtocolException
被引发时,该E.ErrorMessage
属性具有?
而不是预期的特殊字符,即使在使用UTF8Decode()
.
那么,我怎样才能正确解码E.ErrorMessage
?
由于您使用的是 Delphi 7,本机string
类型是AnsiString
,这一点很重要,因为这意味着 Indy 在解码字符串时必须做更多的工作。
当TIdHTTP
将 HTTP 响应主体解析为 时string
,首先使用服务器报告的响应字符集将其解码为 Unicode。例如,如果服务器以 UTF-8 编码发送响应,则需要在标头charset
的属性中指定它Content-Type
。
在 Delphi/FreePascal 的 pre-Unicode 版本中,Unicode 数据然后被转换为 ANSI 以适应AnsiString
. 在这些编译器版本中,TIdHTTP
返回 a 的方法string
有一个可选ADestEncoding
参数,可让您指定AnsiString
要将 Unicode 数据转换为哪种编码。如果不指定,则使用 Indy 的默认编码,默认为 US-ASCII(参见单元GIdDefaultTextEncoding
中的全局变量IdGlobal
)。
你真的应该让 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;
如果您曾经升级到 Unicode 版本的 Delphi,那么您可以简单地删除额外的 UTF-8 步骤:
try
responseBody := IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody);
except
on E: EIdHTTPProtocolException do
responseBody := E.ErrorMessage;
end;
In the example you provided in your question, you are bypassing TIdHTTP
's automated decoding logic, receiving the raw response body as-is into a TStream
instead of a string
. In which case, you are then responsible for making sure you check the response's charset
to know how to decode the raw data properly. It may not always be UTF-8. Indy has ReadStringFromStream()
and ReadStringAsCharset()
functions which allow you to specify an encoding/charset when reading a string
from a TStream
.
Now, to answer your question, why can't you decode the EIdHTTPProtocolException.ErrorMessage
correctly? Well, because it has already been decoded by TIdHTTP
for you.
HOWEVER, here is the rub - when decoding an error response to put into EIdHTTPProtocolException
, the ADestEncoding
parameter is NOT currently accessible from the code that is raising the exception, so Indy's default encoding gets used instead, and that is US-ASCII by default. So that is why you are seeing "special" characters getting converted to ?
(again, this only affects pre-Unicode versions of Delphi/FreePascal).
You have a couple of options to work around this issue:
set the global
IdGlobal.GIdDefaultTextEncoding
variable toencUTF8
before callingPost()
. This way, ifEIdHTTPProtocolException
is raised, itsErrorMessage
will be UTF-8 encoded. Note that this does affect Indy globally, and has much more influence in pre-Unicode versions of Delphi than in Unicode versions, so be careful with it.GIdDefaultTextEncoding := encUTF8; ... try ... responseBody := ...; except on E: EIdHTTPProtocolException do responseBody := UTF8Decode(E.ErrorMessage); end;
since you are saving both success and failure responses to the same
responseBody
variable, you may as well just disable the use ofEIdHTTPProtocolException
altogether, and remove yourtry/except
block. You can do this by enabling thehoNoProtocolErrorException
andhoWantProtocolErrorContent
flags in theTIdHTTP.HTTPOptions
property before callingPost()
. You can check theTIdHTTP.ResponseCode
property to differentiate between success and failure responses:IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent]; responseBody := UTF8Decode( IdHTTP.Post(GetUrlMetodo(ASngpcCloudRequest.Tipo), requestBody, IndyTextEncoding_UTF8) );