如何使 Microsoft XmlHttpRequest 遵守缓存控制指令

发布于 2024-10-21 01:01:36 字数 3131 浏览 7 评论 0原文

我正在使用 MSXML 的 XmlHttpRequest object:

IXMLHttpRequest http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.send();

send 成功,我得到了我的 xml 数据。

除了 XmlHttpRequest 实际上并没有到达网络(我可以看到没有发出实际的 http 请求)。进程监视器显示该文件实际上是从我的缓存中提供的:

在此处输入图像描述

所以我想指示 XmlHttpRequest 用户代理认为任何早于 0 秒的缓存内容都太旧了。执行此操作的标准方法是将请求标头添加

Cache-Control: max-age=0

到发送请求:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

发送成功,我得到了我的xml数据。

除了 XmlHttpRequest 实际上并没有到达网络(我可以看到没有发出实际的 http 请求)。进程监视器显示该文件实际上是从我的缓存中提供的。

那么有什么问题吗? max-age 没有按照我的想法做吗?

来自 RFC 2616 - 超文本传输​​协议,第 14 部分:标头字段定义< /a>:

其他指令允许用户代理 修改基本过期机制。 这些指令可以指定在 请求:

最大年龄
表明该客户端是 愿意接受年龄的答复 不大于指定时间 几秒钟内。除非 max-stale 还包括指令,客户端 不愿意接受陈旧的事物 回应。

这正是我想要的。

Cache-Control: max-age=0 不完全是我想要的,还是 MSXML 的 XmlHttpRequest 对象有问题?

更新一

这是 MSXML XmlHttpRequest COM 对象:

  • CLSID:{88d96a0a-f192-11d4-a65f-0040963251e5}
  • ProgID:Msxml2.XMLHTTP.6.0

更新二

max-age指令由客户端添加以供所有缓存遵守。来自 RFC:

Cache-Control 通用标头字段 用于指定指令 所有缓存都必须遵守 请求/响应机制 链。指令指定行为 旨在防止缓存 对请求产生不利干扰 或回应。这些指令 通常会覆盖默认缓存 算法。缓存指令是 单向的,因为存在 请求中的指令不 暗示同一个指令是 在回复中给出。

Max-age 不适用于服务器;这对于服务器来说没有任何意义。它适用于用户和服务器之间的所有缓存系统。

来自 W3C XmlHttpRequest 的更新三项

如果用户代理实现了 HTTP 缓存,它应该遵守 Cache-Control 请求标头设置 setRequestHeader() (例如, Cache-Control: no-cache 绕过 缓存)。它不得发送Cache-ControlPragma 请求标头 自动,除非最终用户 明确要求此类行为 (例如通过重新加载页面)。

按照他们的示例,我尝试使用no-cache指令:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "no-cache");
http.send();

并且XmlHttpRequest客户端仍然完全从缓存中服务请求,而不查询服务器根本不。

W3C 规定,如果存在缓存,并且通过 setRequestHeader 设置,则必须遵守 Cache-Control。 Microsoft 的 XmlHttpRequest 似乎没有满足该要求。

i'm issuing a request using MSXML's XmlHttpRequest object:

IXMLHttpRequest http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.send();

And the send succeeds, and i get my xml data.

Except that XmlHttpRequest didn't actually hit the network (i can see there no actual http request issued). And Process Monitor shows the file is actually being served from my cache:

enter image description here

So i want to instruct the XmlHttpRequest user agent that any cached content older than 0 seconds is too old. The standards way to do this is to add a request header:

Cache-Control: max-age=0

to the send request:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

And the send succeeds, and i get my xml data.

Except that XmlHttpRequest didn't actually hit the network (i can see there no actual http request issued). And Process Monitor shows the file is actually being served from my cache.

So what is wrong? Is max-age not doing what i think it does?

From RFC 2616 - Hypertext Transfer Protocol, Part 14: Header Field Definitions:

Other directives allow a user agent to
modify the basic expiration mechanism.
These directives MAY be specified on a
request:

max-age
Indicates that the client is
willing to accept a response whose age
is no greater than the specified time
in seconds. Unless max- stale
directive is also included, the client
is not willing to accept a stale
response.

Which exactly what i want.

Is Cache-Control: max-age=0 not exactly what i want, or is MSXML's XmlHttpRequest object buggy?

Update One

This is the MSXML XmlHttpRequest COM object:

  • CLSID: {88d96a0a-f192-11d4-a65f-0040963251e5}
  • ProgID: Msxml2.XMLHTTP.6.0

Update Two

The max-age directive is added by the client for all cache's to adhere to. From RFC:

The Cache-Control general-header field
is used to specify directives that
MUST be obeyed by all caching
mechanisms along the request/response
chain
. The directives specify behavior
intended to prevent caches from
adversely interfering with the request
or response. These directives
typically override the default caching
algorithms. Cache directives are
unidirectional in that the presence of
a directive in a request does not
imply that the same directive is to be
given in the response.

Max-age is not for the server; it makes no sense for a server. It is intended for all caching systems between the user and the server.

Update Three

From W3C XmlHttpRequest:

If the user agent implements a HTTP cache it should respect
Cache-Control request headers set by
the setRequestHeader() (e.g.,
Cache-Control: no-cache bypasses the
cache). It must not send Cache-Control
or Pragma request headers
automatically unless the end user
explicitly requests such behavior
(e.g. by reloading the page).

Following their example, i tried using the no-cache directive:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "no-cache");
http.send();

And the XmlHttpRequest client still services requests completely from the cache, without querying the server at all.

The W3C says that if there is a cache, it must honor Cache-Control if it is set through setRequestHeader. Microsoft's XmlHttpRequest doesn't seem to honor that requirement.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

如果没有你 2024-10-28 01:01:36

不幸的是,XMLHttpRequest 对象就是这样设计的,因为它基于 WinInet。另外,不建议从服务器端使用。您应该使用 ServerXMLHttpRequest ,具有相同的功能,但依赖于 WinHTTP 改为。有关详细信息,请参阅常见问题解答。 ServerXMLHttp 文档中的描述指出:

HTTP 客户端堆栈提供更长的时间
正常运行时间。 WinInet 不具备的功能
对于服务器应用程序至关重要,例如
作为 URL 缓存、自动发现
代理服务器、HTTP/1.1 分块、
离线支持,以及支持
Gopher 和 FTP 协议不是
包含在新的 HTTP 子集中。

这意味着而不是使用 XmlHttpRequest

IXMLHTTPRequest http = CreateComObject("Msxml2.XMLHTTP.6.0");     http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

您可以使用ServerXmlHttpRequest

IXMLHTTPRequest http = CreateComObject("Msxml2.ServerXMLHTTP");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

WinHttpRequest

IWinHttpRequest http = CreateComObject("WinHttp.WinHttpRequest.5.1");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

Unfortunately the XMLHttpRequest object was designed this way, because it is based on WinInet. Also, it is not recommend to be used from the server side. You should use ServerXMLHttpRequest, which has the same functionality, but depends on WinHTTP instead. See the FAQ for more information. A description from the ServerXMLHttp documentation states that:

The HTTP client stack offers longer
uptimes. WinInet features that are not
critical for server applications, such
as URL caching, auto-discovery of
proxy servers, HTTP/1.1 chunking,
offline support, and support for
Gopher and FTP protocols are not
included in the new HTTP subset.

This means that rather than using XmlHttpRequest:

IXMLHTTPRequest http = CreateComObject("Msxml2.XMLHTTP.6.0");     http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

you can use ServerXmlHttpRequest:

IXMLHTTPRequest http = CreateComObject("Msxml2.ServerXMLHTTP");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

or WinHttpRequest:

IWinHttpRequest http = CreateComObject("WinHttp.WinHttpRequest.5.1");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();
风流物 2024-10-28 01:01:36

我发现使用 If-None-Match 标头,指定与最后一个请求的 ETag 不匹配的值将起作用。

例如:

req.open("GET", url, false);
req.setRequestHeader("If-None-Match", "\"doesnt-match-anything\"");
req.send();

这可能需要也可能不需要响应包含 ETag。 (我只尝试过在每个响应中包含 ETag 值的服务。)

I found that using If-None-Match header, specifing a value that does not match the ETag of the last request will work.

Eg:

req.open("GET", url, false);
req.setRequestHeader("If-None-Match", "\"doesnt-match-anything\"");
req.send();

This might or might not require that the responses include an ETag. (I only tried it with a service that includes an ETag value in each response.)

倦话 2024-10-28 01:01:36

您能否在 URI 末尾附加一个随每个请求而变化的虚假参数?

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?requestID=42", False, "", "");

Could you append a bogus parameter on the end of your URI that changes with each request?

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?requestID=42", False, "", "");
夜司空 2024-10-28 01:01:36

我用它来保持活动会话,效果很好。
诀窍是使用标头“If-Modified-Since”,其值比浏览器缓存的值更新。

g_AjaxObj.onreadystatechange = function() { if(g_AjaxObj.readyState === 4) { AjaxOnComplete_("KeepAlive"); }};
g_AjaxObj.open('GET', URL, true);
g_AjaxObj.setRequestHeader("If-Modified-Since", new Date().toUTCString());
g_AjaxObj.send(null);

I use this for a keep-alive session and it works great.
The trick is to use the header "If-Modified-Since" with a value newer that the one cached by the browser.

g_AjaxObj.onreadystatechange = function() { if(g_AjaxObj.readyState === 4) { AjaxOnComplete_("KeepAlive"); }};
g_AjaxObj.open('GET', URL, true);
g_AjaxObj.setRequestHeader("If-Modified-Since", new Date().toUTCString());
g_AjaxObj.send(null);
烟酒忠诚 2024-10-28 01:01:36

我在标准 Windows 客户端上的快速而肮脏的解决方法是
- 互联网选项
- 一般
- 浏览历史设置
- 检查存储页面的较新版本:
挠痒痒“(x)每次访问网页时”
现在我的 Msxml2.XMLHTTP.x.0 对象不再使用缓存......

My quick and dirty workaround at a standard Windows client was
- Internet Options
- General
- Browsing history Settings
- Check for newer Versions of stored pages:
tickle "(x) Every time I visit the Webpage"
Now my Msxml2.XMLHTTP.x.0 Object don't use the Cache anymore ...

娇妻 2024-10-28 01:01:36

该标头用于服务器,并且由于浏览器不会执行任何请求,因此它毫无用处。

一个简单的技巧是像这样加载页面:

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?"+Math.random(), False, "", "");

This header is for the server, and as the browser doesnt event do any request, its useless.

An easy trick is to load the page like this :

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?"+Math.random(), False, "", "");
拍不死你 2024-10-28 01:01:36

对于旧的 msxml 库,我使用随机生成的 uri 地址值,例如:

http://youlink?mysession=random_number

wojtek

For old msxml library, I use random generated value for uri adress eg :

http://youlink?mysession=random_number

wojtek

ζ澈沫 2024-10-28 01:01:36

这样做的缺点是你会用多个副本淹没缓存
相同的内容。这可能是对有缺陷的 http 代理的黑客攻击 - 但是
真正的解决方案是使用缓存机制,而不是反对
他们。 –

我同意这并不理想,也不是真正的解决方案,但 Mozilla 实际上推荐将此作为解决方法,所以我认为它一定不会太糟糕 - https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest

此外,我正在撕扯我的头发试图解决这个问题。我不得不依靠我的用户来清除他们的浏览器(他们总是忘记这样做)。所以这对我来说是天赐之物!

Downside of that is that you flood the cache with multiple copies of
the same content. It might be a hack around buggy http agents - but a
real solution is to work with caching mechanisms, rather than against
them. –

I agree that this is not ideal and not really a solution, but Mozilla actually recommend this as a workaround, so I figure it mustn't be too terrible - https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest

Besides, I was tearing my hair out trying to solve this. I've had to rely on my users to clear their browser (which they keep forgetting to do). So this is a godsend for me!

扛刀软妹 2024-10-28 01:01:36

尝试发送'cache-control: private'作为标头。这对我有用:

var request = new XMLHttpRequest();
request.open("GET", 'http://myurl.com' , false); 

request.setRequestHeader("cache-control", "private");

我正在编写 HTML &适用于 Windows 8 的 Javascript 应用程序,其中 no-cache 和 max-age 都被忽略。对我来说,上面的效果很好。

我不熟悉标题,所以对缓存控制进行了一些挖掘:私有...

Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache, such as a proxy server.

来自 什么是 Cache-Control: private?
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14 .html

因此,基本上,这永远不会创建缓存条目,因此不会添加我们知道是多余的缓存条目,就像“缓存破坏者”随机数参数一样。

Try sending 'cache-control: private' as a header. This worked for me:

var request = new XMLHttpRequest();
request.open("GET", 'http://myurl.com' , false); 

request.setRequestHeader("cache-control", "private");

I'm writing a HTML & Javascript app for Windows 8 where and both no-cache and max-age are ignored. For me the above works just fine.

I wasn't familiar with the header so did a little digging on cache-control: private...

Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache, such as a proxy server.

From What is Cache-Control: private?
and http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

So, basically, this will never create a cache entry and therefore doesn't add cache entries that we know are superfluous, as with a 'cache-buster' random number parameter.

撩人痒 2024-10-28 01:01:36

这让我发疯。这个 SO 线程最接近提供答案。不幸的是,在测试过程中,它们实际上都不适合我。我发现的唯一经过测试可以正常工作的解决方案是设置:

Header Pragma: no-cache

我希望它可以帮助其他人解决 IE 问题。

顺便说一句,这是 StackOverflow 线程,它很好地阐明了 Pragma 和 Cache-control 之间的区别:
Pragma 和 Cache-control 标头之间的区别?

This was driving me crazy. This SO thread came closest to providing an answer. Unfortunately during testing none of them actually worked for me. The only solution I found which tested to work correctly was setting:

Header Pragma: no-cache

I hope it saves others with IE headaches.

BTW this is StackOverflow thread is great to shed light on the difference between Pragma and Cache-control:
Difference between Pragma and Cache-control headers?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文