当 responseText 包含有效 Xml 时,IXMLHttpRequest.responseXml 为空,没有解析错误
Posted
技术标签:
【中文标题】当 responseText 包含有效 Xml 时,IXMLHttpRequest.responseXml 为空,没有解析错误【英文标题】:IXMLHttpRequest.responseXml is empty, with no parse error, when responseText contains valid Xml 【发布时间】:2012-04-14 07:08:45 【问题描述】:我正在从 a government web-site 获取一些 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);
我不会显示所有返回的正文,但它确实会在responseText
中返回有效的 xml:
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
(更重要的是ServerXMLHTTP60
)的行为符合记录?
【问题讨论】:
Delphi 版本信息是所有涉及 RTL 和标准库的问题的关键。什么版本? 我没有使用 Delphi 的任何组件,而是 Delphi 5。 相关:***.com/questions/8925798/… - 注意 Keepalive 评论。试过了吗?另外,什么 IE 版本和 MS XML 版本,因为这些在这些情况下也很重要。我相信 MS XML 的 HTTP 方法使用 WinInet,它有一些有趣的错误,并且当你更新 IE 时它会更新。 @WarrenP 我尝试了超时;它不会改变结果(也不应该,因为我得到了有效的响应)。 ie9,msxml 6.0。如果您复制粘贴最终的简化 8 行版本,您会得到相同的行为吗? 我认为这与您使用 DOMDocument60 而不是http.responseText
有关。
【参考方案1】:
我我发现了问题
我使用Fiddler 将http 响应保存到文本文件。之后我可以修改响应文件,并指示提琴手提供我手工制作的替代品,而不是去原始网站。
经过 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
一旦我发现问题,我就可以回溯the documentation that explain the behavior:
MSXML 6.0 支持的 MIME 类型有:
“文本/xml” “应用程序/xml” 或以“+xml”结尾的任何内容,例如“application/rss+xml”
我正在获取的 RSS 提要实际上是 Resource Definition Format (RDF) feed,其内容类型应该是:
application/rdf+xml
他们的用途:
text/html
在很多层面上都是错误的。
所以我遇到的行为是设计使然;虽然令人沮丧 - 因为没有简单的方法可以知道 responseXml
是否“有效”。
responseXml
对象将被分配
parseError
对象将被分配
parseError.ErrorCode
为零
responseXml.documentElement 将为零
【讨论】:
我的猜测是,由于不支持text/html
,底层 XML 解析器甚至不解析任何数据,这将使 errorCode
设置为零但不会创建 documentElement
对象.如果responseText
不是空的,但documentElement
是空的,那么你就知道出了问题,你可以测试一下。
该站点上的许多中午提要都将Content-Type
发送为text/html
,但其中一些,例如“en_USD.xml”和“en_MXN.xml”,改为发送application/xml
.
那该怎么办呢?通过一些字符串破解来破解他们的 XML?
@WarrenP 我回退到if http.responseXml.documentElement = nil then result := GetXmlObjectFromXmlString(http.responseText) else result := http.responseXml;
这不是一个完全通用的解决方案,因为有时可能会有一个带有 no documentElement 的 xml 文档。但在这种情况下,这就足够了,因为如果 xml 真的是空的,那么他们做了一些愚蠢的事情。【参考方案2】:
YouTube
服务也有同样的问题。
responseXml
对象取决于响应的内容类型/MIME。
您可以检查Content-Type
的响应,例如:如果http.getResponseHeader('Content-Type')
包含text/xml
或application/xml
只有那么您可以参考http.responseXml
,否则它将为空(请参阅MSDN Remarks )。另请注意,出于安全原因,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...
【讨论】:
我不使用 DOMDocument 对象(严格来说我不使用 XmlHttpRequest 对象)因为羊头企业认为阻止人们访问互联网是个好主意(即我必须配置一个代理 - 这只能使用 IServerXmlHttpRequest 完成)。此外,您不能只检查text/xml
,还需要检查application/xml
或anything/anything+xml
(充满了我什至不想关心的边缘情况)。
仔细阅读我的答案。我的意思是,如果响应明确设置为text/xml
,您将有一个有效的responseXml.documentElement
对象。我是说不要依赖responseXml
并始终使用responseText
和DOMDocument
就像在第一个代码示例中一样。当响应内容类型不是 text/xml
时,IServerXmlHttpRequest
的行为与 XmlHttpRequest
相同。
我更喜欢让 XmlHttpRequest 处理响应 xml 文本的解析;否则 xml 必须经过另一个编码周期(编码为 UTF-16 BSTR,然后再次解析)。即使内容类型为text/xml
,documentElement 也可能为 nil - 所以这是一个陷阱。
我也更喜欢那个。但这是设计使然。 ://【参考方案3】:
嗯,这在 Delphi XE 和 Delphi 7 中有效:
procedure TForm1.Button1Click(Sender: TObject);
var
szUrl: string;
http: IXMLHTTPRequest;
doc: $ifndef UNICODEWideString$elsestring$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 字符都会变成 ?在你身上,在非 unicode delphi 版本中。
【讨论】:
respoonseText
有效,responseXml
为空(尽管没有解析错误)【参考方案4】:
您正在从DOMDocument
对象本身检索xml
,但您应该从文档树中的第一个节点获取它,例如:
doc := http.responseXml as DOMDocument60;
Assert(doc.parseError.errorCode = 0);
ShowMessage('"' + doc.DocumentElement.childNodes.Item(0).xml + '"');
Microsoft 自己在 DOMDocument
和 xml
属性的文档中的示例正好显示了这种逻辑。
【讨论】:
在这种情况下,documentElement
为零。
嗯,这就解释了为什么xml
是空的——DOMDocument
中什么都没有。
至少它是骗人的:DOMDocument 中没有任何内容,但它是有效的 XML。以上是关于当 responseText 包含有效 Xml 时,IXMLHttpRequest.responseXml 为空,没有解析错误的主要内容,如果未能解决你的问题,请参考以下文章
ajax 技术有一事不明有高人指点,当我们用responseText接收response.write输出的值时有长度限制吗?
Ajax 服务器响应 XMLHttpRequest | AJAX 教程
Xmlhttprequest.responseText返回的是啥格式