ajax php flush 失效,Ajax+PHP缓存遇到的坑

之前设计了一个ajax跨域请求json数据的功能,最近由于访问量增大需要在php程序中对前端返回的json信息进行缓存,于是写了一个缓存层,将如下信息进行缓存:<?php

// 获取缓存文件路径

$cache_path = get_json_filepath();

// 检查缓存文件是否存在或过期

if(!file_exists($cache_path) || (filemtime($cache_path) + $cache_time 

{

// 从数据库中获取$json

$json = get_json_from_database();

// 写缓存

file_put_contents($cache_path, jsonp_callback($json));

echo jsonp_callback($json);

}else{

// 直接从缓存中读取并返回给前端

echo file_get_contents($cache_path);

}

// 跨域json解决方案

function jsonp_callback($json)

{

if(isset($_GET['callback']))

{

return $_GET['callback']."($json)";

}else{

return "jsonp_callback($json)";

}

}

由于之前的设计是通过jquery的$.getJSON()跨域获取的数据,所以在json上包装了一层callback函数名。添加完简单的缓存层之后就遇到问题了。

第一次请求(无任何缓存时)前端js可以正常工作,通过Chrome——Network——Response可以得到正常的字符串jsonp1451045842617({json数据}),执行了$.getJSON()的回调函数。

当然,此时php已经写好了缓存文件,第二次请求时直接从缓存中读取数据并返回给前端,可是并没有执行$.getJSON()的回调函数。

关键问题:此时浏览器没有任何报错,而且通过Chrome——Network——Response同样可以得到正常的字符串jsonp1451045842617({json数据})。

然后我删除了服务器上的缓存并重新测试,并将两次请求的字符串复制到两份文件并逐一比较,发现两次请求的Response完全相同,然后就纳闷了,为何两次ajax请求的结果完全相同的Response情况下却只有第一次生效?

问题就出在这两份完全相同的Response上,php文件中每一次$_GET['callback']的值都不一样(毫秒数不停的增长),而php返回给前端的函数名(始终为某个时刻的毫秒数)已经过期了,当然无法触发$.getJSON()回调了。

解决方案很简单,在写php缓存的时候不要带上毫秒数函数名,只对json字符串进行缓存:<?php

// 获取缓存文件路径

$cache_path = get_json_filepath();

// 检查缓存文件是否存在或过期

if(!file_exists($cache_path) || (filemtime($cache_path) + $cache_time 

{

// 从数据库中获取$json

$json = get_json_from_database();

// 写缓存, 不包括函数名

file_put_contents($cache_path, $json);

}else{

// 直接从缓存中读取json数据

$json = file_get_contents($cache_path);

}

echo jsonp_callback($json);

// 跨域json解决方案

function jsonp_callback($json)

{

if(isset($_GET['callback']))

{

return $_GET['callback']."($json)";

}else{

return "jsonp_callback($json)";

}

}

另外,如果没有jquery库的情况下我们仍然可以自行实现jsonp跨域,js代码还可以这样写:

var JSONP = {

// 获取当前时间戳

now: function() {

return (new Date()).getTime();

},

// 获取16位随机数

rand: function() {

return Math.random().toString().substr(2);

},

// 删除节点元素

removeElem: function(elem) {

var parent = elem.parentNode;

if(parent && parent.nodeType !== 11) {

parent.removeChild(elem);

}

},

// url组装

parseData: function(data) {

var ret = "";

if(typeof data === "string") {

ret = data;

}

else if(typeof data === "object") {

for(var key in data) {

ret += "&" + key + "=" + encodeURIComponent(data[key]);

}

}

// 加个时间戳,防止缓存

ret += "&_time=" + this.now();

ret = ret.substr(1);

return ret;

},

getJSON: function(url, data, func) {

// 函数名称

var name;

// 拼装url

url = url + (url.indexOf("?") === -1 ? "?" : "&") + this.parseData(data);

// 检测callback的函数名是否已经定义

var match = /callback=(\w+)/.exec(url);

if(match && match[1]) {

name = match[1];

} else {

// 如果未定义函数名的话随机成一个函数名

// 随机生成的函数名通过时间戳拼16位随机数的方式,重名的概率基本为0

// 如:jsonp_1355750852040_8260732076596469

name = "jsonp_" + this.now() + '_' + this.rand();

// 把callback中的?替换成函数名

url = url.replace("callback=?", "callback="+name);

// 处理?被encode的情况

url = url.replace("callback=%3F", "callback="+name);

}

// 创建一个script元素

var script = document.createElement("script");

script.type = "text/javascript";

// 设置要远程的url

script.src = url;

// 设置id,为了后面可以删除这个元素

script.id = "id_" + name;

// 把传进来的函数重新组装,并把它设置为全局函数,远程就是调用这个函数

window[name] = function(json) {

// 执行这个函数后,要销毁这个函数

window[name] = undefined;

// 获取这个script的元素

var elem = document.getElementById("id_" + name);

// 删除head里面插入的script,这三步都是为了不影响污染整个DOM啊

JSONP.removeElem(elem);

// 执行传入的的函数

func(json);

};

// 在head里面插入script元素

var head = document.getElementsByTagName("head");

if(head && head[0]) {

head[0].appendChild(script);

}

}

};

JSONP.getJSON("http://127.0.0.1/table.php?callback=?", "a=b&c=d", function(json) {

console.log(json)

});

相应的php后端代码如下:<?php

$json = json_encode(array(

"html" => "Happy Christmas"

));

echo jsonp_callback($json);

// 跨域json解决方案

function jsonp_callback($json)

{

if(isset($_GET['callback']))

{

return $_GET['callback']."($json)";

}else{

return "jsonp_callback($json)";

}

}

原理上和jquery基本相同,只不过在这里在毫秒数之后又添加了2位随机数,这样出现同一毫秒请求2次的几率更小了。

仔细读一下上面的js代码,你会对jsonp的原理有一个比较深刻的认识。

转载随意,但请附上文章地址:-)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值