zoukankan      html  css  js  c++  java
  • Ajax跨域原理及解决方案

    跨域请求的产生

    跨域请求归根结底是由于浏览器的“同源策略”引起的,同源策略指的是域名相同协议相同端口相同, 假设有http://www.a.com/test.html,下面的示例 域名不同

    http://demo.a.com/test1.html

    协议不同

    https://www.a.com/test2.html

    端口号不同

    http://www.a.com:8090/test3.html

    实验文件列表

                 

    先介绍一下文件列表:

    • a_ajax.html是在域名a.ajax.com下使用
    • b_ajax.html是在域名b.ajax.com下使用
    • ajax.html是在域名www.ajax.com下使用
    • ajax2.html是在域名www.ajax2.com下使用
    • result.php是公共使用返回文件

    跨子域解决方案

    http://a.ajax.com/a_ajax.html请求http://b.ajax.com/result.php,通过iframe来加载http://b.ajax.com/b_ajax.html,同时来提升域的级别,a_ajax.html示例代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>a_ajax</title>
        <script type="text/javascript">
            document.domain = "ajax.com";
        </script>
    </head>
    <body>
    <h1>a.ajax.com</h1>
    <iframe src="http://b.ajax.com/b_ajax.html"></iframe>
    </body>
    </html>

    b.ajax.html示例代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>b_ajax</title>
        <script src="jquery.min.js"></script>
        <script type="text/javascript">
            document.domain = 'ajax.com';
            function test() {
                $.ajax({
                    type: 'GET',
                    url: 'http://b.ajax.com/result.php',
                    data: {
                        username: 'qiaoweizhen'
                    },
                    dataType: 'json',
                    success: function (response) {
                        console.log(response);
                    }
                });
            }
        </script>
    </head>
    <body>
    <h1>b.ajax.com</h1>
    <a href="javascript:test();">Submit</a>
    </body>
    </html>

    上述例子通过document.domain = 'ajax.com'来将a.ajax.comb.ajax.com提升到同一个根域ajax.com下面来实现跨域。

    jsonp解决方案

    http://www.ajax.com/ajax.html请求http://www.ajax2.com/result.php,示例代码 ajax.html

    function test() {
        $.ajax({
            type: 'GET',
            url: 'http://www.ajax2.com/result.php',
            data: {
                username: 'qiaoweizhen'
            },
            dataType: 'json',
            success: function (response) {
                console.log(response);
            }
        });
    }

    result.php文件示例代码:

    <?php
    $fp = fopen('log.txt', 'w+');
    fwrite($fp, print_r($_REQUEST, true));
    fclose($fp);

    如果使用上述代码去请求的话,先查看url,只有一个我们自己使用的参数username

    然后查看报错信息:

    然后观察服务器端没有产生任何日志文件,则说明在浏览器端就被禁用掉了。如果将请求改成jsonp代码如下:

    function test() {
        $.ajax({
            type: 'GET',
            url: 'http://www.ajax2.com/result.php',
            data: {
                username: 'qiaoweizhen'
            },
            dataType: 'jsonp',
            success: function (response) {
                console.log(response);
            }
        });
    }

    发起请求的话,就会发现url有所变化,使用jquery会默认增加callback_参数:

    查看控制台输出:

    而查看服务端日志,如下:

    说明两者之间的通信是没有问题,就需要将处理的结果返回给客户端,可以修改result.php代码,示例如下:

    <?php
    $fp = fopen('log.txt', 'w+');
    fwrite($fp, print_r($_REQUEST, true));
    fclose($fp);
    $callback = $_REQUEST['callback'];
    $username = $_REQUEST['username'];
    $response = [
        'status'  => 1,
        'message' => 'ok',
        'data'    => [
            'username' => $username
        ]
    ];
    echo $callback . '(' . json_encode($response) . ')';

    ok,上面的实例就完成了通过jsonp进行跨域请求的操作,有以下几点需要说明:

    • jsonp只支持GET请求,这是因为jsonp请求,归根结底是通过动态从服务端加载一段JavaScript脚本来在客户端执行,返回实例:jQuery183014317321930723415_1483925970177({"status":1,"message":"ok","data":{"username":"qiaoweizhen"}})
    • jsonp中的callback是可以自定义的,只需要和dataType同级别参数jsonpCallback,如jsonpCallback: 'myCallback',在定义一个myCallback函数就可以了
    • 可不可以在data中传入callback参数,是可以的,但是容易混淆,而且服务端接收参数的时候只能接收到一个callback参数,不建议这么做

    CORS(跨域资源共享)解决方案

    上面通过jsonp来实现了简单的跨域请求,但是会面临以下几个问题:

    • 请求来源是否合法
    • 请求数据大小有限制,因为是GET请求
    • jsonp不提供错误处理机制,即动态加载的返回代码是有问题的话,没有相应的处理机制 解决上面最好的方式是通过CORS,经常使用服务端两个参数Access-Control-Allow-Origin(配置域名)Access-Control-Request-Method(配置方法)。如http://www.ajax.com/ajax.html请求http://www.ajax2.com/result.php,ajax.html示例代码如下:
    function test() {
        $.ajax({
            type: 'GET',
            url: 'http://www.ajax2.com/result.php',
            data: {
                username: 'qiaoweizhen'
            },
            dataType: 'json',
            success: function (response) {
                console.log(response);
            }
        });
    }

    服务端通过设置返回头信息,来进行一定的限制,服务端result.php示例代码如下:

    <?php
    header('Access-Control-Allow-Origin:*');

    这样子就允许所有域名进行ajax跨域请求,如果需要限制,示例代码如下:

    <?php
    $allowOrigins = [
        'http://www.ajax.com',
        'http://a.ajax.com'
    ];
    if (in_array($_SERVER['HTTP_ORIGIN'], $allowOrigins)) {
        header('Access-Control-Allow-Origin:' . $_SERVER['HTTP_ORIGIN']);
        echo json_encode([
            'status'  => 1,
            'message' => 'ok'
        ]);
    }

    上面的是通过简单请求,如果非简单请求,服务器会先通过预检机制来进行限制,ajax.html示例代码:

    function test() {
        $.ajax({
            type: 'PUT',
            url: 'http://www.ajax2.com/result.php',
            data: {
                username: 'qiaoweizhen'
            },
            dataType: 'json',
            success: function (response) {
                console.log(response);
            }
        });
    }

    如果发送了一个PUT请求,查看请求url,发现是Request Method:OPTIONS而不是Request Method:PUT,这是因为浏览器和服务器之间进行了一次预检通信机制:

    如果服务端设置了,可以使用PUT请求,服务端result.php代码如下:

    $allowOrigins = [
        'http://www.ajax.com',
        'http://a.ajax.com'
    ];
    header('Access-Control-Allow-Origin:*');
    header('Access-Control-Allow-Methods:PUT');

    那么就会发生两次请求,一次是预检机制的OPTIONS请求,一次是真正的PUT请求

  • 相关阅读:
    在IIS上搭建WebSocket服务器(一)
    ngnix 一 入门指南
    博客园的打赏功能
    Java基础(十)内部类
    Java虚拟机之垃圾回收详解一
    Servlet问题:servlet cannot be resolved to a type解决办法
    WIN7局域网文件共享设置方法
    基于tcp的socket通信
    java基础之抽象类的介绍
    Hdfs详解
  • 原文地址:https://www.cnblogs.com/lonmyblog/p/8394729.html
Copyright © 2011-2022 走看看