CI Output类BUG? 附完美解决方案
本帖最后由 ciogao 于 2012-4-29 17:28 编辑一个项目中开发接口的需求为RESTful,返回资源类型为JSON。
使用Output类设置header头信息:
$this->output->set_header('Content-type: application/json',true);
$this->output->set_status_header(200);
返回资源类型正常,见图:
data/attachment/album/201204/12/182449u76g472gugp62255.png
为缓存某接口返回信息,使用output->cache()方法,首次返回资源类型正常,第二次在cache未过期时,cache方法取得缓存,并展示,资源类型为 html,返回资源类型不正确,接口服务异常。见图:
data/attachment/album/201204/12/182449qf8g8j8c8uri8gjn.png
初步判断为Output类中读取cache并输出的方法有误,未正确设置资源类型。
可否认定为Output类BUG?
事实上,CI 如果发现有缓存不会经过控制器,所以你的 set_header 方法不会被执行,当然就无法发送正确的头。
这个原因实际上是由于 CI 的缓存比较简单,没有考虑还要缓存 http header。
如果算 BUG 的话,我想应该给缓存文件加上 http header 的信息才行。 Hex 发表于 2012-4-12 20:26 static/image/common/back.gif
事实上,CI 如果发现有缓存不会经过控制器,所以你的 set_header 方法不会被执行,当然就无法发送正确的头 ...
对的,正是因为cache方法在取得缓存文件后,输出时没有输出header信息 Hex 发表于 2012-4-12 20:26 static/image/common/back.gif
事实上,CI 如果发现有缓存不会经过控制器,所以你的 set_header 方法不会被执行,当然就无法发送正确的头 ...
翻看了Output类与核心文件CodeIgniter.php,
Output类中初始化头信息为空:
class CI_Output {
var $final_output;
var $cache_expiration = 0;
var $headers = array();
var $enable_profiler = FALSE;
于是可修改$headers为
var $headers = array(array('Content-type: application/json',true));
或CodeIgniter.php中修改缓存输出头:
$OUT =& load_class('Output');
/*
* ------------------------------------------------------
* Is there a valid cache file?If so, we're done...
* ------------------------------------------------------
*/
if ($EXT->_call_hook('cache_override') === FALSE)
{
$OUT->set_header('Content-type: application/json',true); //为缓存输出header,资源为json
if ($OUT->_display_cache($CFG, $URI) == TRUE)
{
exit;
}
}
此接口返回缓存cache时资源类型为json的问题解决。
但此时要输出html类型缓存又成为了问题,因为所有的缓存输出都成为了json,html代码不被解析。
拟采取的方法:以上两处恢复原样初始设置不做任何修改,修改output类中_display_cache与_display方法,
将缓存生成规则修改为
时间戳 + 资源类型 + cache
取得cache时,正则匹配出“资源类型”,同时输出头信息。
今天晚了,明天照此优化output类,完成后分享具体修改方法与优化后类文件。 ciogao 发表于 2012-4-13 01:06 static/image/common/back.gif
翻看了Output类与核心文件CodeIgniter.php,
Output类中初始化头信息为空:
你这个方法才是治本的方法。
把 HTTP HEADER 也缓存起来,这样就能缓存各种数据了。 本帖最后由 ciogao 于 2012-5-1 22:12 编辑
Hex 发表于 2012-4-13 12:10 static/image/common/back.gif
你这个方法才是治本的方法。
把 HTTP HEADER 也缓存起来,这样就能缓存各种数据了。 ...
这段时间一直忙于一个项目的一系列接口的开发工作,加班甚至通宵,没有时间修改Output类,今天抽了点时间看了一下。修改完毕。分享给大家,以下是修改步骤。(HEX申请加精啊!{:soso_e100:})
首先修改一下类的注释信息:
* @package CodeIgniter
* @author ExpressionEngine Dev Team
* @author ciogao@gmail.com 修改于 2012-04-29 缓存多种资源类型
对写缓存方法_write_cache的修改如下,详情见注释:
/**
* Write a Cache File
* @authorciogao@gmail.com 修改于 2012-04-29
* @access public
* @return void
*/
function _write_cache($output)
{
$CI =& get_instance();
$path = $CI->config->item('cache_path');
$cache_path = ($path == '') ? BASEPATH.'cache/' : $path;
if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
{
return;
}
$uri = $CI->config->item('base_url').
$CI->config->item('index_page').
$CI->uri->uri_string();
$cache_path .= md5($uri);
if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
{
log_message('error', "Unable to write cache file: ".$cache_path);
return;
}
$expire = time() + ($this->cache_expiration * 60);
//=====================
//Powered by ciogao@gmail.com
//判断有无设置头,并写入如 jsonFORMATS--->xmlFORMATS--->
if ($this->headers) {
$formats = explode('/',$this->headers);
$formats = $formats.'FORMATS--->';
}
//======================
if (flock($fp, LOCK_EX))
{
fwrite($fp, $expire.'TS--->'.$formats.$output); //写入 formats
flock($fp, LOCK_UN);
}
else
{
log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
return;
}
fclose($fp);
@chmod($cache_path, DIR_WRITE_MODE);
log_message('debug', "Cache file written: ".$cache_path);
}
此时如果视图中使用了set_herder方法,如:
$this->output->set_header('Content-type: application/json',true);
则缓存文件中会加入资源类型描述,生成缓存如:
1335690070TS--->jsonFORMATS--->{"code":1000,"msg":"success","data":{"17":{"custname":"\u5c01\u795e\u9879\u76ee8"," ***
缓存成功。
接下来修改_display_cache方法,详情见注释:
/**
* Update/serve a cached file
* @authorciogao@gmail.com 修改于 2012-04-29
* @access public
* @return void
*/
function _display_cache(&$CFG, &$URI)
{
$cache_path = ($CFG->item('cache_path') == '') ? BASEPATH.'cache/' : $CFG->item('cache_path');
if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
{
return FALSE;
}
// Build the file path.The file name is an MD5 hash of the full URI
$uri = $CFG->item('base_url').
$CFG->item('index_page').
$URI->uri_string;
$filepath = $cache_path.md5($uri);
if ( ! @file_exists($filepath))
{
return FALSE;
}
if ( ! $fp = @fopen($filepath, FOPEN_READ))
{
return FALSE;
}
flock($fp, LOCK_SH);
$cache = '';
if (filesize($filepath) > 0)
{
$cache = fread($fp, filesize($filepath));
}
flock($fp, LOCK_UN);
fclose($fp);
// Strip out the embedded timestamp
if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
{
return FALSE;
}
// Has the file expired? If so we'll delete it.
if (time() >= trim(str_replace('TS--->', '', $match['1'])))
{
@unlink($filepath);
log_message('debug', "Cache file has expired. File deleted");
return FALSE;
}
//================================================================
/**
* Powered by ciogao@gmail.com 2012-07-4-29
* 获取缓存信息中的资源类型,并设置输出头信息
*/
preg_match("/(\w+FORMATS--->)/", $cache, $formats);
if ($formats) {
$formats_tem = explode('FORMATS',$formats);
switch ($formats_tem){
case 'json':
$header = 'application/json'; //jsonRPC
break;
case 'xml':
$header = 'application/xml'; //xmlRPC
break;
case 'plain':
$header = 'text/plain'; //PHP数组
break;
default:
$header = 'test/html'; //其他判断为html
}
$cache = str_replace($formats,'',$cache);
$this->set_header('Content-type: '.$header,true);
}
//================================================================
// Display the cache
$this->_display(str_replace($match['0'], '', $cache));
log_message('debug', "Cache file is current. Sending it to browser.");
return TRUE;
}
OK,大功告成。
使用不明白的地方可以联系我,以下是联系方式:
ciogao@gmail.com msn或email均可
新浪微博 @阳光蝙蝠
$formats = $formats.'FORMATS--->';
这一句 应该是
$output .= $formats.'FORMATS--->' . $output;
才对吧?
页:
[1]