周末在家仔细研究了一下ajax跨域的问题,虽然可以用jsonp但jsonp只能用于GET请求,所以还是深入了一下。
CORS的具体内容大家可网上搜一下,这里主要是设置一个名为“Access-Control-Expose-Headers”的报头对响应报头进行授权。PHP中使用header("Access-Control-Allow-Origin:*");就可以了。
thinkphp中 控制器返回之前加一行header("Access-Control-Allow-Origin:*");也能成功,但是如果每个控制器函数都写这么一句就太费事了。所以创建了一个AjaxController类继承Controller类,并在构造函数中添加了
class AjaxController extends Controller {
public function __construct(){
parent::__construct();//调用父类的方法,因为这里重写了父类的方法
//用于解决前台ajax跨域请求
header("Access-Control-Allow-Origin:*");
//header("Access-Control-Allow-Origin:http://111.com");
}
}
其他控制器全部继承此类
class IndexController extends AjaxController {
测试成功。但又发现另一个问题
thinkphp文档里提到的success和error方法会自动判断当前请求是否属于Ajax请求,如果属于Ajax请求则会调用ajaxReturn方法返回信息。 ajax方式下面,success和error方法会封装下面的数据返回:
经过一翻排查发现success并没有以ajax方式返回。这里有两个解决方法,一是指定success的第三个参数为true(error同理)。
但这样就失去了灵活性,本着一挖到底的原则最后找到了\ThinkPHP\Library\Think\Controller.class.php中的
private function dispatchJump($message,$status=1,$jumpUrl='',$ajax=false) {
if(true === $ajax || IS_AJAX )
这里用于判断是否为ajax提交过来的。IS_AJAX在\ThinkPHP\Library\Think\App.class.php中,内容为
define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')])) ? true : false);
看来IS_AJAX并不能很好的反映是否为ajax提交。所以在App.class.php又增加了IS_AJAX2的定义
switch ($_SERVER['HTTP_ACCEPT']){//定义常量IS_AJAX2 用于判断ajax跨域提交
case 'application/json, text/javascript, */*':
// JSON 格式
define('IS_AJAX2',true);
break;
case 'text/javascript, application/javascript, */*':
// javascript 或 JSONP 格式
define('IS_AJAX2',true);
break;
case 'text/html, */*':
// HTML 格式
break;
case 'application/xml, text/xml, */*':
// XML 格式
break;
}
并修改Controller.class.php中dispatchJump函数
private function dispatchJump($message,$status=1,$jumpUrl='',$ajax=false) {
if(true === $ajax || IS_AJAX ||IS_AJAX2) {//IS_AJAX2 用于判断ajax跨域提交
这样问题就彻底解决了,但因为改动了框架函数,对于以后升级带了不便,不知道有没有其他更好的方案。
还有另一个需要修改的地方就是Controller.class.php中的ajaxReturn函数也要增加header("Access-Control-Allow-Origin:*"),不然的使用$this->ajaxreturn也不支持跨域
protected function ajaxReturn($data,$type='',$json_option=0) {
if(empty($type)) $type = C('DEFAULT_AJAX_RETURN');
switch (strtoupper($type)){
case 'JSON' :
// 返回JSON数据格式到客户端 包含状态信息
header('Content-Type:application/json; charset=utf-8');
header("Access-Control-Allow-Origin:*");//用于解决前台ajax跨域请求 libo
exit(json_encode($data,$json_option));
case 'XML' :
// 返回xml格式数据
header('Content-Type:text/xml; charset=utf-8');
exit(xml_encode($data));
case 'JSONP':
// 返回JSON数据格式到客户端 包含状态信息
header('Content-Type:application/json; charset=utf-8');
header("Access-Control-Allow-Origin:*");//用于解决前台ajax跨域请求 libo
$handler = isset($_GET[C('VAR_JSONP_HANDLER')]) ? $_GET[C('VAR_JSONP_HANDLER')] : C('DEFAULT_JSONP_HANDLER');
exit($handler.'('.json_encode($data,$json_option).');');
case 'EVAL' :
再说一下前台的$.ajax需要注意的就是 在IE10中textStatus一直为parsererror,导致结果一直为error,测试发现是浏览器安全设置问题。然后在jquery官方找到了$.ajax的crossDomain: true,这个参数,但还是没效果。最后用了jQuery.support.cors = true;
jQuery.support.cors = true;//提供了这样的方式来支持浏览器的跨站请求 IE10有效
$.ajax({
url: "http://xxxx/Home/index/ajaxlogin",
data: {"uid":$name,"upw":$pw},
timeout: 5000,
cache: false,
type: "POST",
dataType: "json",//如果返回类型与dataType不同,会使textStatus为parsererror而导致执行error函数
crossDomain: true,//Jquery1.5之后,提供了这样的方式来支持浏览器的跨站请求。(经测试IE10及以下无效,但在ajax请求前设置jQuery.support.cors = true;有效)