清漆 3:接受 JSON returns HTML

Varnish 3: Accept JSON returns HTML

我试着做一个 REST-API,但是 varnish returns 总是第一个被调用的响应,我不知道为什么。

如果我用浏览器打开一个页面,Varnish returns HTML -> 就可以了。 如果我卷曲同一页 curl -i https://example.com -H "Accept: application/json" Varnish 也 returns HTML -> 这是错误的。

如我所见,Varnish 总是 returns 第一个缓存项,如果这是 JSON varnish returns JSON,如果这是 HTML Varnish returns HTML.

没有清漆,一切都按预期工作。

如果您在同一 URL 上提供不同的内容类型,您可能需要告诉 Varnish 相应地对缓存进行分区。

事实上,Varnish 并没有做太多特别的事情,它的行为与其他代理一样。如果他们看到 URL 没有指定资源缓存应如何分区的信息,那么无论是 JSON 还是常规请求,无论请求类型如何,第一个请求都将被缓存并提供相同的服务.

所以你需要告诉 Varnish 如何为资源分区缓存。

"Vary" header

最直接且 "HTTP" 兼容其他代理的方法是 Vary response header。

它告诉代理缓存(在本例中为 Varnish)根据 的 header 值对资源进行分区, 改变 缓存来自客户.

例如客户端发送 header X: some-value 并且您的应用发送 header Vary: X 是它需要的使缓存在 X.

的不同值之间不同

对于 Varnish 3,有一个 Accept-Encoding 的例子。

文章详细介绍了 Vary 的实施挑战 - 不同的客户端可能会针对不同的 header 发送完全不同的值,从而导致缓存严重分区。因此,您通常希望将变化的 header 的值标准化为一组已知的预期值。

在您的情况下,您想要改变(并规范化)Accept header。所以按照(在 vcl_recv 过程中)的思路:

if (req.http.Accept) {
    if (req.http.Accept ~ "application/json") {
        set req.http.Accept = "application/json";
    } else {
        set req.http.Accept = "text/html";
    }
}

接下来您需要让您的应用实际发送 Vary: Accept(在您的应用源文件中)。或者,如果修改应用程序源文件不可行,您可以抛出一些 Varnish VCL:

sub vcl_fetch {
    if (!beresp.http.Vary) { # no Vary at all
        set beresp.http.Vary = "Accept";
    } elseif (beresp.http.Vary !~ "Accept") { # add to existing Vary
        set beresp.http.Vary = beresp.http.Vary + ", Accept";
    }
}