Ajax 对通过 cloudflare 提供的 vue 资源的响应数据未在 Chrome 中从 json 解析

Ajax response data to vue resource served via cloudflare not being parsed from json in Chrome

我有一个部署到 Heroku 的站点,我使用 Cloudflare 作为代理。

对于一些 front-end 行为,我使用 Vue 和 Vue-resource 来执行一些 ajax 请求。响应的格式为 JSON 和 Content-Type:application/json header.

奇怪的是,当我的脚本出错时,因为 vue-resource 没有将响应解析为 javascript array/object,尽管这是预期的行为。但是,只有通过 Cloudflare URL 访问站点时才会出现此问题。如果我从 Heroku url(例如 foo-bar-1234.herokuapp.com)访问它,一切正常。它在我的本地开发环境(Vagrant、Nginx)上也运行良好。

问题也只发生在Chrome。无论是否是 Cloudflare,它在 Safari 和 Firefox 中都可以正常工作。

我认为问题一定出在 CloudFlare 对 Heroku 服务器的响应所做的某些事情上,该服务器阻止 Chrome 中的 Vue 资源正确解析响应。

此处参考来自 CloudFlare-served 请求的响应:

cache-control:no-cache
cf-ray:2d2f1980573d3476-LHR
content-encoding:gzip
content-type:application/json
date:Mon, 15 Aug 2016 19:37:10 GMT
server:cloudflare-nginx
set-cookie:sam_session=[redacted]; expires=Mon, 15-Aug-2016 21:37:10 GMT; Max-Age=7200; path=/; HttpOnly
status:200
via:1.1 vegur
x-ratelimit-limit:60
x-ratelimit-remaining:59

对比 heroku-served 一个:

Cache-Control:no-cache
Connection:keep-alive
Content-Type:application/json
Date:Mon, 15 Aug 2016 19:40:10 GMT
Server:Apache
Set-Cookie:sam_session=[redacted]; expires=Mon, 15-Aug-2016 21:40:10 GMT; Max-Age=7200; path=/; HttpOnly
Transfer-Encoding:chunked
Via:1.1 vegur
X-Ratelimit-Limit:60
X-Ratelimit-Remaining:58

header 看起来非常相似,所以我想不出反应有何不同会导致这种情况...

感谢任何调试建议。

更新:编译好的javascript可以在这里看到(大文件):https://github.com/samarkanddesign/samarkand-web/blob/master/public/js/admin.js

这是一个很大的文件,所以最好看看 repo:https://github.com/samarkanddesign/samarkand-web/tree/master/resources/assets/js

更新 2:在两种情况下执行的 vue-resource ajax 请求示例:

  1. CloudFlare

  2. 直接来自 Heroku:

我们可以在 heroku 示例中看到响应 data 被解析为 objects 的数组,而在 CloudFlare 中它是一个字符串。

我认为问题出在 Vue-Resource 以及 CloudFlare(SPDY 协议)使用 lower-case 响应 headers 而 Heroku Apache 使用大写的事实。

Vue 资源在其拦截器中错误地忽略了小写 headers,导致它无法解析响应 body。

此错误问题中引用了此内容:https://github.com/vuejs/vue-resource/issues/317

一种快速修补它的方法是通过自定义拦截器:

Vue.http.interceptors.unshift(function(request, next) {
    next(function(response) {
        if(typeof response.headers['content-type'] != 'undefined') {
            response.headers['Content-Type'] = response.headers['content-type'];
        }
    });
});