用户
 找回密码
 入住 CI 中国社区
搜索
查看: 13803|回复: 18
收起左侧

[讨论/交流] 关于ajax的问题(整理回答,发布正确的代码,csrf)

    [复制链接]
发表于 2011-12-14 11:58:24 | 显示全部楼层 |阅读模式
本帖最后由 elftail 于 2011-12-16 00:04 编辑

在这里把我遇到的ajax问题和大家的回答整理总结一下, 刚学CI, 可能有错误,希望指正。

如果你有以下问题,可能会得到帮助:

1. 跨域访问错误
2. csrf保护开启后ajax出现错误
3. gZip开启后出现ajax错误

测试环境
Ubuntu: firefox, chrome
windows XP: IE, 360浏览器的IE8

1. 跨域访问
(下面的问题经常出现在你载入ajax视图和处理ajax请求是同一个控制器时,是特殊情况,

但因为这是我遇到的第一个问题,而且折磨了我很久,所以还是写出来了,希望别人能避开这个错误)

(1)当ajax的请求地址控制器处理程序的地址不一致时会出现错误。

如 ajax在域名'www.444.com'的视图里, 请求的是 'www.333.com/ci/index.php/test/ajax' ,
则会出现跨域访问错误,我的提示是 301 Moved Permanently

(2) localhost和127.0.0.1是不同的域

解释:
这个是javascript的安全限制,不能跨域。
(感谢 huboo82的回答)

localhost也叫local ,正确的解释是:本地服务器
127.0.0.1在windows等系统的正确解释是:本机地址(本机服务器)
localhot(local)是不经网卡传输!这点很重要,它不受网络防火墙和网卡相关的的限制。
127.0.0.1是通过网卡传输,依赖网卡,并受到网络防火墙和网卡相关的限制。
(感谢credochen的回答)

(3) www.ddd.com 和 ddd.com 是不同的域。
解释:
如果ajax请求的地址和控制器的地址里www不一致,则出错.
(不知道是不是我电脑的错误,我在视图 a.php请求的是 "www.ddd.com/ci/index.php/test/ajax", 如果我在url用 “ddd.com/ci/index.php/test”控制器载入视图,然后用同一个控制器处理ajax请求就出错,加上www就没有错。)

(4) ajax请求如果是完整路径,则加上"http://",不然Codeigniter会自动在你的地址前面
加上默认路径变成 http://www.ddd.com/ci/index.php/test/www.ddd.com/ci/index.php
而这可能不是你的本意

我的解决方法: (不是很好,但是有用)
1)载入视图的和处理ajax请求的分成不同的控制器,这样ajax的请求就不用担心与调用的,
控制器地址不一样.

2)如果你把调用ajax视图和处理请求的都放到同一个控制器文件里的话.

PHP复制代码
 
        var body = $("body");
        var baseUrl = body.context.baseURI;
 
复制代码


context.baseURI 会得到载入本视图的地址,这样就能和服务其上的地址一致,避免一些错误,
但这个没有把所有情况考虑进去,你需要自己对这个地址设定一些条件检查。

2.csrf 保护的问题
config里设置 $config['csrf_protection'] = TRUE;  会对post进行 csrf保护,
他会对用户提交的数据进行检查,可以提高安全,建议开启。当然会引发一些问题。

(1)简单的说就是你提交的数据中如果没有特定的值给控制器,就回出错。
-----------------------------------------------------------------
详细的解释: (这里可以看原帖
感谢c361239752的回答
最近研究CI源码,正好看到CSRF保护,原理是这样的:在config中设置了开始CSRF保护,用form生成表单会生成hidden的一段HASH值,这段HASH会被写入到cookies中源码如下:
setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);
其中cookie_domain作用域是config中设定的,提交POST后会判断post过来的token和cookies中的token是否存在,再判断这两个值是否相等,否则显示错误页面,判断完后立即unset($_POST[$this->_csrf_token_name]);unset($_COOKIE[$this->_csrf_cookie_name]);以避免污染$_POST数据
-------------------------------------------------------------------

不要被这个解释给吓倒了,其实你要做的只是把一个变量加到你的ajax的提交的数据里,
这个变量定义在你的 $config['csrf_token_name'] = 'csrf_test_name';
这里主要提交的数据是 csrf_test_name的值,Codeigniter会自动添加到Cookie里.

那如何得到哪个变量和值?
这个会返回你要的值
<?php echo $this->security->get_csrf_hash(); ?>

这个会返回你要传递的变量名(就是你设置在$config['csrf_token_name']
<?php echo $this->security->get_csrf_token_name(); ?>

给个实例代码? (我们都喜欢具体的实例代码不是吗? {:soso_e112:})
PHP复制代码
 
            $.ajax({
                type: "OST",
                // 这里是你的请求地址
                url: "www.ddd.com/ci/index.php/test/ajax",
                data: "name="+cv+'&'+"<?php echo $this->security->get_csrf_token_name(); ?>"+'='+"<?php echo $this->security->get_csrf_hash(); ?>",
                success: function(msg){
                    mydiv.html(msg).show();
                },  
                error: function() {
                   alert("ajax error")
                ;}
            });
 
 
复制代码

(2) 表单提交时出错

如果你开启了csrf,然后直接用纯 <from ..></form>格式提交表单就会出错.原因同上

解决方法
1)用Codeigniter的 form_open() 函数,codeigniter会自动把验证代码插入到视图表单里,然后隐藏并
跟你的数据一起提交。(推荐)
2)用上面的方法获得数据,然后提交.

相关资料:
http://blog.hsin.tw/2011/codeigniter-csrf-protection-form-ajax/
这个博客文章的解决方法很好,可惜代码有错误,不过还是值得参考(我就是从这里得到帮助,解决了问题)
http://codeigniter.com/forums/viewthread/163976/
在这里我找到了解决方法的代码.

3. gZip压缩开启
如果你设置了$config['compress_output'] = TRUE; 不能在任何控制器里使用 echo
因为如果你在控制器用echo输出的方法返回ajax数据,就回出现编码错误

解决方法:
1. (推荐)
$data = "your ajax return data";
$this->output->set_output($data); (推荐)
2. $this->load->view('data'); (会把视图内容传输给客户端)

最后让我们贴出激动人心的完整的美丽的强大的......实例代码,用于参考
(我在Ubuntu的firefox和chrome,还有xp里的IE里测试过,不保证其他浏览器里也正确
我使用了jQuery, 请确保jQuery包含了进来,当然你也可以改成纯js.)

视图. ajax.php

HTML复制代码
 
<!DOCTYPE HTML>
<html lang="en">
<head>
    <script type="text/javascript"     src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
 
 
    <script type="text/javascript">
    $(document).ready(function() {
 
        var mydiv = $("#myDiv");
 
 
        function swapContent(cv) {
            // ajax请求时的等待图片可以写在这里
            mydiv.html("ut animated .gif here").show();
 
            // 因为我调用这个视图的和处理这个ajax的控制器是同一个,所以
            // 可能会出现跨域错误
            var u = "http://127.0.0.1/ci/index.php/test/ajax";
 
            $.ajax({
                type: "OST",
                url: u,
                data: "name="+cv+'&'+"<?php echo $this->security->get_csrf_token_name(); ?>"+'='+"<?php echo $this->security->get_csrf_hash(); ?>",
                success: function(msg){
                    mydiv.html(msg).show();
                },  
                error: function() {alert("ajax error");}
            });
        }
 
        $("#c1").click(function() {
            swapContent("d1");
        });
 
        $("#c2").click(function() {
            swapContent("d2");
        });
 
        $("#c3").click(function() {
            swapContent("d3");
        });
 
    });
    </script>
        <meta charset="UTF-8">
        <title></title>
</head>
<body>
    <a id ="c1" href="#" >Content1</a>
    <a id = "c2" href="#" >Content2</a>
    <a id = "c3" href="#" >Content3</a>
    <div id="myDiv">My default content</div>
 
</body>
</html>
 
 
复制代码



控制器 test.php



PHP复制代码
<?php
 
class Test extends CI_Controller {
 
    public function __construct(){
        parent::__construct();
    }
     public function index()
     {
        $this->load->view('ajax');
     }
 
     function ajax()
     {
        $name = $this->input->post('name');
 
        $this->output->set_output('server get data: '.$name);
     }
}
 
 
复制代码



(全文完)
:wq!

还有如果有其他在写ajax时需要注意的地方或者经验,欢迎一起分享,不胜感激.

----------------------------------------------------------------------------------------
第一次时发问题的帖子,没删,保留.


经过firphp 调试,发现数据送到服务器,但是firebug上提示
http://localhost:8888/ci/index.php/test/ajax    200 OK  的错误提示,并执行error函数
对ajax 刚接触,不知道哪里出错了,请大家帮忙看一下,谢谢


这个问题已经解决,要把localhost 改成 127.0.0.1 不过不知道为什么要这样
回答:这个是javascript的安全限制,不能跨域。(感谢 huboo82的回答)
-----------------------------------------------------------------------------------------







发表于 2017-12-8 05:42:18 | 显示全部楼层
如果是跨域post 请求了。
发表于 2014-10-24 16:51:53 | 显示全部楼层
原来是因为这个啊
发表于 2011-12-14 15:50:39 | 显示全部楼层
是否启用了csrf,如果有csrf,post的请求必须得把csrf的值传回去。
 楼主| 发表于 2011-12-14 16:25:59 | 显示全部楼层
huboo82 发表于 2011-12-14 15:50
是否启用了csrf,如果有csrf,post的请求必须得把csrf的值传回去。

感谢你的回答,我已经找到问题所在,
把 localhost 改成  127.0.0.1 就行 ,不知道什么原因?
发表于 2011-12-14 16:31:27 | 显示全部楼层
这个是javascript的安全限制,不能跨域。
 楼主| 发表于 2011-12-14 16:36:11 | 显示全部楼层
huboo82 发表于 2011-12-14 16:31
这个是javascript的安全限制,不能跨域。

哦,localhost 和 127.0.0.1 不算做同一个域吗? 学习了,谢谢
发表于 2011-12-15 08:45:48 | 显示全部楼层
200是表示正常的吧
发表于 2011-12-15 09:33:18 | 显示全部楼层
以下是我在网上看到的:
localhost也叫local ,正确的解释是:本地服务器
127.0.0.1在windows等系统的正确解释是:本机地址(本机服务器)

localhot(local)是不经网卡传输!这点很重要,它不受网络防火墙和网卡相关的的限制。

127.0.0.1是通过网卡传输,依赖网卡,并受到网络防火墙和网卡相关的限制。
发表于 2011-12-15 10:24:47 | 显示全部楼层
ajax最近刚刚在学,帮你顶一下
 楼主| 发表于 2011-12-15 19:27:21 | 显示全部楼层
longjianghu 发表于 2011-12-15 08:45
200是表示正常的吧

恩, 200表示 服务器返回正常,是客户端的 js错了,是在 域上出的错
 楼主| 发表于 2011-12-15 19:27:57 | 显示全部楼层
credochen 发表于 2011-12-15 09:33
以下是我在网上看到的:
localhost也叫local ,正确的解释是:本地服务器
127.0.0.1在windows等系统的正确 ...

谢谢你的解释,学习了

本版积分规则