如何阻止 Indy 将 XML 中的字符转换为代码

How to stop Indy from converting characters in XML to codes

我正在尝试使用第三方网站 API,它希望请求正文是 XML 文档,但服务器接收的 XML 是由于格式不正确而被拒绝,因为 Indy 已经用 ANSI 代码替换了一堆字符。

我正在使用 XE2 和 Indy 10.5.8.0。

我已经注意到 Indy 正在将 CRLF 转换为 &,因此将所有这些都从源代码中删除 XML。现在,当我传递给 .Post 的 TStringList 包含:

<?xml version="1.0" encoding="utf-8"?><AdCourierAPI>

服务器正在查看:

%3C%3Fxml+version=%221.0%22+encoding%3D%22utf-8%22%3F%3E%3CAdCourierAPI%3E

如何阻止 Indy 这样做?我需要更改编码吗?

这里是 API 方法之一的代码:

function TBroadBeanAPI.ListChannels(const AUserName, APassword: String): String;
var
  HTTP: TIdHTTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
  ASource: TStringList;
  XMLRequest: String;
begin
  HTTP := TIdHTTP.Create(nil);
  SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
  ASource := TStringList.Create;

  Result := '';

  try
    HTTP.IOHandler := SSL;
    HTTP.Request.ContentType := 'text/xml';
    HTTP.Request.Charset := 'utf-8';

    XMLRequest := '<?xml version="1.0" encoding="utf-8"?>'+
                  '<AdCourierAPI>'+
                  '    <Method>ListChannels</Method>'+
                  '    <APIKey>'+APIKEY+'</APIKey>'+
                  '    <Account>'+
                  '        <UserName>'+AUserName+'</UserName>'+
                  '        <Password>'+APassword+'</Password>'+
                  '    </Account>'+
                  '</AdCourierAPI>';

    ASource.Text := XMLRequest;
    Result := HTTP.Post(APIURL, ASource);
  finally
    SSL.Free;
    HTTP.Free;
    ASource.Free;
  end;
end;

答案是关闭TIdHTTP对象的HTTPOptions中的[hoForceEncodeParams]。可以这样做:

HTTP.HTTPOptions := [];

您一开始就使用了错误的 Post() 版本。

您正在使用接受 TStrings 作为输入的 Post() 的重载版本。该版本旨在以 application/x-www-form-urlencoded 格式发布 HTML 网络表单,其中 TStrings 应包含 name=value 字符串,这些字符串根据 W3C 提交网络表单的规则进行编码。这就是为什么您会看到 XML 按原样编码的原因。

您需要使用接受 TStream 作为输入的 Post() 的另一个重载版本。 TStream 数据将按原样传输。

试试这个:

function TBroadBeanAPI.ListChannels(const AUserName, APassword: String): String;
var
  HTTP: TIdHTTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
  ASource: TStringStream;
  XMLRequest: String;
begin
  Result := '';
  HTTP := TIdHTTP.Create(nil);
  try
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
    HTTP.IOHandler := SSL;
    HTTP.Request.ContentType := 'text/xml';
    HTTP.Request.Charset := 'utf-8';

    XMLRequest := '<?xml version="1.0" encoding="utf-8"?>'+
                  '<AdCourierAPI>'+
                  '    <Method>ListChannels</Method>'+
                  '    <APIKey>'+APIKEY+'</APIKey>'+
                  '    <Account>'+
                  '        <UserName>'+AUserName+'</UserName>'+
                  '        <Password>'+APassword+'</Password>'+
                  '    </Account>'+
                  '</AdCourierAPI>';

    ASource := TStringStream.Create(XMLRequest, TEncoding.UTF8);
    try
      Result := HTTP.Post(APIURL, ASource);
    finally
      ASource.Free;
    end;
  finally
    HTTP.Free;
  end;
end;

附带说明一下,您在构建 XML 时并未对 APIKEYAUserNameAPassword 进行编码。如果它们包含保留字符,则根据 XML 格式规则,它们必须 escaped/encoded。所以要当心。您应该使用真正的 XML 引擎来构建您的 XML,而不是使用字符串连接。否则,至少添加一个 XML 兼容的编码函数,以便您可以连接正确格式的字符串。