IXMLHttpRequest.responseXml пуст, без ошибки синтаксического анализа, когда responseText содержит допустимый Xml

я получаю некоторые XML из правительственный веб-сайт:

http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml

я использую следующий, довольно простой код:

var
   szUrl: string;
   http: IXMLHTTPRequest;
begin
   szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';

   http := CoXMLHTTP60.Create;
   http.open('GET', szUrl, False, '', '');
   http.send(EmptyParam);

   Assert(http.Status = 200);

   Memo1.Lines.Add('HTTP/1.1 '+IntToStr(http.status)+' '+http.statusText);
   Memo1.Lines.Add(http.getAllResponseHeaders);
   Memo1.Lines.Add(http.responseText);

я не буду показывать все тело, которое возвращается, но оно возвращает действительный xml в responseText:

HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3


<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns="http://purl.org/rss/1.0/"
    xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:dcterms="http://purl.org/dc/terms/"
    xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.w3c.org/1999/02/22-rdf-syntax-ns#rdf.xsd">
    <channel rdf:about="http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_ALL.xml">
        <title xml:lang="en">Bank of Canada: Noon Foreign Exchange Rates</title>
        <link>http://www.bankofcanada.ca/rates/exchange/noon-rates-5-day/</link>

Хорошо, хорошо,там есть действительный xml. я знаю, что это действует так...ты только посмотри. Но я также знаю, что это действительно, разбирая его:

var
   ...
   szXml: WideString;
   doc: DOMDocument60;
begin
   ...
   szXml := http.responseText;

   doc.loadXML(szXml);
   Assert(doc.parseError.errorCode = 0);

   Memo1.Lines.Add('============parsed xml');
   Memo1.Lines.Add(doc.xml);

в origianal IXmlHttpRequest содержит responseXml собственность. Из MSDN:

представляет тело анализируемого объекта ответа.

если тело сущности ответа не является допустимым XML, это свойство возвращает DOMDocument, который был проанализирован, так что можно получить доступ к ошибке. Это свойство не возвращает сам IXMLDOMParseError, но он доступен из DOMDocument.

в моем случае свойство responseXml существует, так как оно следует:

Assert(http.responseXml <> nil);

и нет ошибки разбора responseText:

doc := http.responseXml as DOMDocument60;
Assert(doc.parseError.errorCode = 0);

как и должно быть, так как xml действителен.

кроме того, что когда я смотрю на http.responseXml объект документа, он пуст:

   Memo1.Lines.Add('============responseXml');
   Memo1.Lines.Add(doc.xml);

Is is IXMLHttpRequest (и IXMLServerHttpRequest), возвращающий пустой XML-документ, когда:

  • есть xml
  • xml действителен
  • нет ошибки разбора

в длинной форме:

uses
    msxml2_tlb;

procedure TForm1.Button1Click(Sender: TObject);
var
    szUrl: string;
    http: IXMLHTTPRequest;
    doc: DOMDocument60;
begin
    szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';

    http := CoXMLHTTP60.Create; //or CoServerXmlHttpRequest.Create
    http.open('GET', szUrl, False, '', '');
    http.send(EmptyParam);

    Assert(http.Status = 200);

    doc := http.responseXml as DOMDocument60;
    Assert(doc.parseError.errorCode = 0);

    ShowMessage('"'+doc.xml+'"');
end;

как сделать XmlHttpRequest (и что более важно ServerXMLHTTP60) вести себя как документально?

4 ответов


Яя нашел проблему

я Саша для сохранения ответа http в текстовый файл. После этого я мог бы изменить файл ответов и проинструктировать fiddler, чтобы он обслуживал мои созданные вручную альтернативы, а не переходил на исходный веб-сайт.

enter image description here

после 3 часов возни мне удалось отследить проблему в исходных заголовках http-ответов:

HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3

должны быть:

HTTP/1.1 200 OK
Cache-Control: max-age=5
Connection: keep-alive
Connection: Transfer-Encoding
Date: Fri, 30 Mar 2012 14:50:50 GMT
Transfer-Encoding: chunked
Content-Type: text/xml; charset=UTF-8
Expires: Fri, 30 Mar 2012 14:50:55 GMT
Server: Apache/2.2.16 (Unix) PHP/5.3.3 mod_ssl/2.2.16 OpenSSL/1.0.0d mod_perl/2.0.4 Perl/v5.12.0
X-Powered-By: PHP/5.3.3

как только я нашел проблему, я смог вернуться-найти документация, объясняющая поведение:

поддерживаемые типы MIME для MSXML 6.0:

  • " text / xml"
  • " application / xml"
  • или все, что заканчивается " + xml", например " приложение / rss + xml"

RSS-канал, который я получаю, на самом деле лента формата определения ресурсов (RDF), где тип контента должен быть:

application/rdf+xml

использование:

text/html

ошибается на стольких уровнях.

таким образом, поведение, которое я испытываю, является преднамеренным; хотя и расстраивает - так как нет простого способа узнать, если responseXml "допустимо".

  • the

у меня была такая же проблема с YouTube услуги.

на responseXml объект зависит от типа содержимого / MIME ответа.
Вы могли бы изучить ответ Content-Type Эл.G: если http.getResponseHeader('Content-Type') содержит text/xml или application/xml и только потом вы можете обратиться к http.responseXml, в противном случае он будет пустым (см. MSDN Примечания). Также обратите внимание, что responseXml функции проверки анализатора всегда отключены для обеспечения безопасности причины.

но на http.responseText будет всегда есть xml текст, независимо от того, какой тип контента находится в ответе, поэтому вы можете всегда используйте новый экземпляр DOMDocument загрузить xml Эл.г:

...
http := CoXMLHTTP60.Create; // or CoServerXmlHttpRequest.Create 
http.open('GET', szUrl, False, '', '');
http.send(EmptyParam);
Assert(http.Status = 200);

doc := CreateOleObject('Msxml2.DOMDocument.6.0') as DOMDocument60; 
doc.async := False;
doc.loadXML(http.responseText); // <- load XmlHttpRequest.responseText into DOMDocument60 and use it
Assert(doc.parseError.errorCode = 0);

// do useful things with doc object...

Ну, это работает в Delphi XE и Delphi 7:

procedure TForm1.Button1Click(Sender: TObject);
var
    szUrl: string;
    http: IXMLHTTPRequest;
    doc: {$ifndef UNICODE}WideString{$else}string{$endif};
begin
    szUrl := 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_all.xml';

    http := CoXMLHTTP60.Create; //or CoServerXmlHttpRequest.Create
    http.open('GET', szUrl, False, '', '');
    http.setRequestHeader('Content-Type', 'text/xml;charset=UTF-8');
    http.send(EmptyParam);

    Assert(http.Status = 200);

    doc := UTF8Encode(http.responseText);

    Memo1.Lines.text := doc;
//  ShowMessage('"'+doc.xml+'"');
end;

надеюсь, что это сработает и для вас в Delphi 5. Конечно, любые символы unicode собираются превратиться в ? на вас, в версиях delphi, отличных от unicode.


вы отступаете xml С DOMDocument сам объект, но вы должны захватить его с первого узла в дереве документа, например:

doc := http.responseXml as DOMDocument60; 
Assert(doc.parseError.errorCode = 0); 
ShowMessage('"' + doc.DocumentElement.childNodes.Item(0).xml + '"'); 

собственные примеры Microsoft в документации для DOMDocument и xml показать именно такую логику.