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);

Оригинальный 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);

Это 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< /a> (и, что более важно, ServerXMLHTTP60) вести себя как задокументировано?


person Ian Boyd    schedule 30.03.2012    source источник
comment
Информация о версии Delphi является ключевой во всех вопросах, связанных с RTL и стандартными библиотеками. Какая версия?   -  person Warren P    schedule 30.03.2012
comment
я не использую никаких компонентов от Delphi, но Delphi 5.   -  person Ian Boyd    schedule 30.03.2012
comment
Связанный: stackoverflow.com/questions/8925798/ — обратите внимание на комментарий Keepalive. Пробовали это? Кроме того, какая версия IE и версия MS XML, так как они тоже имеют значение в этих случаях. Я считаю, что HTTP-методы MS XML используют WinInet, в котором есть несколько забавных ошибок, и он обновляется при обновлении IE.   -  person Warren P    schedule 30.03.2012
comment
@WarrenP я попробовал тайм-аут; это не меняет результат (и не должно, так как я получаю правильный ответ). т.е.9, msxml 6.0. Будет ли такое же поведение, если вы скопируете и вставите окончательную упрощенную 8-строчную версию?   -  person Ian Boyd    schedule 30.03.2012
comment
Думаю, это как-то связано с тем, что вы используете DOMDocument60 вместо http.responseText.   -  person Warren P    schedule 30.03.2012
comment
@WarrenP я думаю, ты что-то пропустил; я хочу использовать document (а не responseText)   -  person Ian Boyd    schedule 30.03.2012


Ответы (4)


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

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

введите здесь описание изображения

После 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:

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

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

application/rdf+xml

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

text/html

неправильно на очень многих уровнях.

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

  • объект responseXml будет присвоен
  • объект parseError будет присвоен
  • parseError.ErrorCode равно нулю
  • responseXml.documentElement будет равен нулю
person Ian Boyd    schedule 30.03.2012
comment
Я предполагаю, что, поскольку text/html не поддерживается, базовый синтаксический анализатор XML даже не анализирует какие-либо данные, что оставит errorCode равным нулю, но не создаст объект documentElement. Если responseText не пусто, а documentElement пусто, вы знаете, что что-то пошло не так, и вы можете это проверить. - person Remy Lebeau; 31.03.2012
comment
Многие ленты Noon на этом сайте отправляют Content-Type как text/html, но некоторые из них, например en_USD.xml и en_MXN.xml, вместо этого отправляют application/xml. - person Remy Lebeau; 31.03.2012
comment
Так что делать? Взломать их XML с помощью взлома строк? - person Warren P; 31.03.2012
comment
@WarrenP Я возвращаюсь к if http.responseXml.documentElement = nil then result := GetXmlObjectFromXmlString(http.responseText) else result := http.responseXml; Это не совсем универсальное решение, так как иногда может быть документ xml с no documentElement. Но в данном случае это достаточно хорошо, потому что если xml действительно пуст, значит, они сделали что-то глупое. - person Ian Boyd; 31.03.2012

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

Объект responseXml зависит от типа содержимого/MIME ответа.
Вы можете проверить ответ Content-Type, например: если 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...
person kobik    schedule 31.03.2012
comment
я не использую объект DOMDocument (и, строго говоря, я не использую объект XmlHttpRequest), потому что предприятие бараньих голов считает хорошей идеей блокировать доступ людей к Интернету (т.е. я должен настроить прокси-сервер, который можно только с помощью IServerXmlHttpRequest). Кроме того, вы не можете просто проверить text/xml, нужно также проверить application/xml или anything/anything+xml (из-за крайних случаев, о которых я даже не хочу заботиться). - person Ian Boyd; 01.04.2012
comment
Прочитайте мой ответ внимательно. Я говорю, что у вас будет действительный объект responseXml.documentElement только, если ответ явно установлен на text/xml. Я говорю НЕ полагайтесь на responseXml и всегда используйте responseText и DOMDocument как в первом примере кода. IServerXmlHttpRequest ведет себя так же, как XmlHttpRequest, когда тип содержимого ответа не text/xml. - person kobik; 01.04.2012
comment
я бы предпочел позволить XmlHttpRequest обрабатывать синтаксический анализ XML-текста ответа; в противном случае xml должен пройти еще один цикл кодирования (кодирование как UTF-16 BSTR, а затем повторный анализ). Возможно, что documentElement равен нулю, даже если тип содержимого равен text/xml, так что это подвох. - person Ian Boyd; 01.04.2012
comment
Я бы тоже так предпочел. Но это по дизайну. :/ - person kobik; 01.04.2012

Ну, это работает в 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. Конечно, любые символы Юникода превратятся в ? на вас, в версиях delphi без юникода.

person Warren P    schedule 30.03.2012
comment
respoonseText работает, а responseXml пустое (правда ошибки разбора нет) - person Ian Boyd; 30.03.2012

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

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

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

person Remy Lebeau    schedule 30.03.2012
comment
В этом случае documentElement равно нулю. - person Ian Boyd; 30.03.2012
comment
Что ж, тогда это объясняет, почему xml пусто — в DOMDocument ничего нет. - person Remy Lebeau; 31.03.2012
comment
По крайней мере, это обманчиво: в DOMDocument ничего нет, но это правильный XML. - person Ian Boyd; 31.03.2012