大约一年前,我们写过一篇关于新的fetch() API的文章。WHATWG Fetch API 提供了一种现代化的网络资源获取方式,并允许您对请求和响应的详细信息进行精细控制。如果您不熟悉 Fetch API,建议您在继续之前阅读相关内容。
我们最近在 Fetch API 中添加了一些新功能,本文将概述这些功能,并给出如何利用它们开发 Web 应用程序的示例。
Referrer 控制 API
使用 fetch(),您现在可以控制 HTTP 请求referrer 和referrer 策略。HTTP Referer [sic] 标头是一个(拼写错误的!)标头,它允许目标页面知道用户来自哪个源页面(例如,通过点击该页面上的链接)。这对于收集关于您的网站用户来自哪里 的分析数据非常有用。
referrer 策略是一个新的 W3C 规范,我们一直在 Firefox 中实现该规范,它允许页面向浏览器提供一个策略,使页面能够更好地控制 Referer 标头的设置方式。有几种不同的策略状态,每种状态都具有特定的目标。以下是一个总结。
“no-referrer”
阻止发送任何 Referer 标头。当您出于隐私原因想要隐藏 Referer 标头时,这可能很有用。例如,一些搜索引擎在 URL 中添加了有关用户搜索词和其他信息的额外信息,他们可能不希望将用户的搜索词泄露给用户点击的搜索结果网站。 “no-referrer” referrer 策略可以用于此目的。“no-referrer-when-downgrade”
类似于 “no-referrer”,区别在于它仅在从安全上下文导航到非安全上下文时才会省略 Referer 标头。例如,在上面的搜索引擎示例中,如果您担心的是人们监视 HTTP 流量而不是目标网站,则可以使用 “no-referrer-when-downgrade” 策略。在这种情况下,如果搜索结果链接到安全上下文,浏览器将发送 Referer 标头,但如果目标网站是非安全 HTTP 网站,浏览器将拒绝以明文发送 Referer 标头。如果未指定显式策略,这是默认策略。“origin”
将使浏览器仅在 Referer 标头中包含引用源,而不包含完整的 URL。例如,如果您希望目标网站能够知道用户来自您的搜索结果页面,而不透露完整的 URL,则可以使用 “origin”。在这种情况下,浏览器将从 Referer 标头中发送的 URL 中删除域名后面的所有内容。“origin-when-cross-origin”
类似于 “origin”,但它仅在跨源导航时才会删除完整的 URL。例如,假设您只想在跨源导航时(例如,如果您网站正在执行正常的网页搜索,我们假设您的搜索结果页面是跨源的)包含您的搜索结果页面的源,而发送完整的 referrer 到您自己的内部页面。这可以让您自己的分析软件了解您的用户如何跨越您网站的页面导航。在这种情况下,“origin-when-cross-origin” 是正确的策略选择。“unsafe-url”
会导致浏览器将完整的 URL(不包含任何相关的用户名、密码或片段)发送到用户导航到的所有页面,无论它们是跨源还是安全。它被称为unsafe 的真正原因是它会将完整的 URL 透露给任何目标网页,这会引发隐私问题,例如上面的例子试图解决的问题。如果可能,您应该考虑使用其他 referrer 策略。
现在,在 Firefox 中,您可以在页面上使用<meta name=referrer> 元素为从页面发起的的所有网络请求设置全局 referrer 策略。我们还正在努力实现逐元素 referrer 策略属性,这在您想对特定元素(例如 <img>)使用不同的 referrer 策略时很有用。借助此处介绍的新 API,您还可以控制使用 fetch() 下载的资源的 referrer 和 referrer 策略。
以下代码示例展示了如何使用这些新的 fetch() 功能的一些示例。
// Let’s assume that the code below runs on https://example.site/page.html
// Download a json but don't reveal who is downloading it
fetch("sneaky.json", {referrerPolicy: "no-referrer"})
.then(function(response) { /* consume the response */ });
// Download a json but pretend another page is downloading it
fetch("sneaky.json", {referrer: "https://example.site/fake.html"})
.then(function(response) { /* consume the response */ });
// You can only set same-origin referrers.
fetch("sneaky.json", {referrer: "https://cross.origin/page.html"})
.catch(function(exc) {
// exc.name == "TypeError"
// exc.message == "Referrer URL https://cross.origin/page.html cannot be cross-origin to the entry settings object (https://example.site)."
});
// Download a potentially cross-origin json and don't reveal
// the full referrer URL across origins
fetch(jsonURL, {referrerPolicy: "origin-when-cross-origin"})
.then(function(response) { /* consume the response */ });
// Download a potentially cross-origin json and reveal a
// fake referrer URL on your own origin only.
fetch(jsonURL, {referrer: "https://example.site/fake.html",
referrerPolicy: "origin-when-cross-origin"})
.then(function(response) { /* consume the response */ });
// Override sending the document global referrer policy set using
// to send the full referrer URL.
// Be careful!
fetch(jsonURL, {referrerPolicy: "unsafe-url"})
.then(function(response) { /* consume the response */ });
如果您的网站使用service worker,那么您可以检查获取资源时附带的 referrer 和 referrer 策略。使用fetch 事件处理程序 内部的referrer 和referrerPolicy 属性,检查Request 对象。
此 API 将在 Firefox 47 中可用,该版本目前可在开发者版发布通道 中进行测试。
Fetch 缓存控制 API
通过 fetch() 下载的资源,类似于浏览器下载的其他资源,会受到HTTP 缓存 的影响。这通常是可以的,因为这意味着如果您的浏览器有一个缓存的 HTTP 请求响应副本,它可以使用缓存的副本,而不是浪费时间和带宽从远程服务器重新下载。
但是,在某些情况下,您可能希望对浏览器是否使用 HTTP 缓存进行一些控制。您可以通过清除要获取的资源的 URL 缓存 来确保无论浏览器 HTTP 缓存中有什么,您都能获得最新的响应,将其获取到service worker 控制的缓存 中。这通常通过在下载之前将参数(例如 'cache-bust=' + Date.now()
)附加到 URL 来完成,但这非常丑陋。现在有一种更好的方法可以使用 fetch 缓存控制 API 来完成。
此 API 的理念是为 fetch 指定一个缓存策略,以明确指示何时以及如何使用浏览器 HTTP 缓存。要有效地使用这些策略,需要充分了解 HTTP 缓存语义。网上有很多关于 HTTP 缓存语义的文章,例如这篇文章,其中详细描述了这些语义。目前,您可以从五种不同的策略中进行选择。
“default”
表示在下载资源时使用浏览器的默认行为。浏览器首先查看 HTTP 缓存中是否有匹配的请求。如果有,并且是新鲜的,它将从 fetch() 返回。如果存在但已过时,则会向远程服务器发出条件请求,如果服务器指示响应未更改,则将从 HTTP 缓存中读取。否则,它将从网络下载,并使用新响应更新 HTTP 缓存。“no-store”
表示完全绕过 HTTP 缓存。这将使浏览器在进入网络时不查看 HTTP 缓存,并且永远不会将生成的响应存储在 HTTP 缓存中。使用此缓存模式,fetch() 的行为就像不存在 HTTP 缓存一样。“reload”
表示在进入网络时绕过 HTTP 缓存,但会使用新下载的响应更新它。这将导致浏览器在进入网络时永远不查看 HTTP 缓存,但会使用下载的响应更新 HTTP 缓存。如果合适,将来的请求可以使用该更新的响应。“no-cache”
表示即使浏览器认为响应是新鲜的,也始终验证 HTTP 缓存中的响应。这将导致浏览器在进入网络时查找 HTTP 缓存中是否有匹配的请求。如果找到了这样的请求,浏览器将始终创建一个条件请求来验证它,即使它认为响应应该是新鲜的。如果没有找到匹配的缓存条目,则会发出正常请求。下载响应后,HTTP 缓存将始终使用该响应进行更新。“force-cache”
表示如果在缓存中找到了匹配的条目,浏览器将始终使用缓存的响应,而忽略响应的有效性。因此,即使在缓存中找到了非常旧版本的响应,它也将始终使用它,而不会进行验证。如果在缓存中找不到匹配的条目,浏览器将发出正常请求,并使用下载的响应更新 HTTP 缓存。
让我们看一些关于如何使用这些缓存模式的示例。
// Download a resource with cache busting, to bypass the cache
// completely.
fetch("some.json", {cache: "no-store"})
.then(function(response) { /* consume the response */ });
// Download a resource with cache busting, but update the HTTP
// cache with the downloaded resource.
fetch("some.json", {cache: "reload"})
.then(function(response) { /* consume the response */ });
// Download a resource with cache busting when dealing with a
// properly configured server that will send the correct ETag
// and Date headers and properly handle If-Modified-Since and
// If-None-Match request headers, therefore we can rely on the
// validation to guarantee a fresh response.
fetch("some.json", {cache: "no-cache"})
.then(function(response) { /* consume the response */ });
// Download a resource with economics in mind! Prefer a cached
// albeit stale response to conserve as much bandwidth as possible.
fetch("some.json", {cache: "force-cache"})
.then(function(response) { /* consume the response */ });
此 API 计划在 Firefox 48 中发布,目前已在 Firefox Nightly 中提供测试。
关于 Ehsan Akhgari
Ehsan 自 2006 年以来一直致力于 Firefox 的各个部分。现在他大部分时间都花在 Gecko 和 Web API 上。
14条评论