美文网首页JavaScript
前端跨域请求方法整理

前端跨域请求方法整理

作者: Cause_XL | 来源:发表于2017-05-25 14:35 被阅读0次

    前端跨域请求方法整理

    什么是跨域请求

    浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用。一般的,只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用。

    我们利用 NodeJs 创建了两个服务器,分别监听 3000、 3001 端口(下面简称 服务器3000 与 服务器3001 ),由于端口号不一样,这两个服务器以及服务器上页面通信构成了跨域请求。

    在服务器3000 上有如下的页面:

    ajaximage.png

    服务器3000 上的请求页面中包含如下 JavaScript 代码:

    $(function () {
      $("#submit").click(function () {
        var data = {
          name: $("#name").val(),
          id: $("#id").val()
        };
        $.ajax({
          type: 'POST',
          data: data,
          url: 'http://localhost:3000/ajax/deal',
          dataType: 'json',
          cache: false,
          timeout: 5000,
          success: function (data) {
            console.log(data)
          },
          error: function (jqXHR, textStatus, errorThrown) {
            console.log('error ' + textStatus + ' ' + errorThrown);
          }
        });
      });
    });
    

    服务器3000 对应的处理函数为:

    app.post('/ajax/deal', function (req, res) {
      console.log('server accept', req.body.name, req.body.id);
      var data = {
        name: req.body.name + '- server 3000 process',
        id: req.body.id + '- server 3000 process'
      };
      res.send(data);
      res.end();
    })
    

    请求页面返回结果:

    ajaximage2.png

    此处数据处理成功。

    此时让 服务器3000 上的页面向 服务器 3001 发起请求:

    $.ajax({
        ...
        url: 'http://localhost:3001/ajax/deal',
        ...
    });
    

    服务器3001 对应的处理函数与 服务器3000 类似:

    app.post('/ajax/deal', function (req, res) {
      console.log('server accept', req.body.name, req.body.id);
      var data = {
        name: req.body.name + '- server 3001 process',
        id: req.body.id + '- server 3001 process'
      };
      res.send(data);
      res.end();
    })
    

    结果如下:

    ajaximage3.png

    结果由于端口号不同,发生了跨域请求。

    需要注意的是,服务器 3001 控制台有输出:

    server accept:  chiaki 3001
    

    这说明跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器返回的结果会被浏览器拦截。

    利用 JSONP 实现跨域调用

    JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题。其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 script 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。

    1.使用 jQuery 集成的 $.ajax 实现 JSONP 跨域调用

    依然是上面的例子,我们将 服务器 3000 上的请求页面的 JavaScript 代码改为:

    // 回调函数
    function jsonpCallback(data) {
      console.log('jsonpCallback:' + data.name)
    }
    
    $("#submit").click(function () {
      var data = {
        name: $("#name").val(),
        id: $("#id").val()
      };
      $.ajax({
        // type: 'POST',
        data: data,
        url: 'http://localhost:3000/ajax/deal',
        dataType: 'jsonp', //修改为jsonp
        cache: false,
        timeout: 5000,
        // jsonp 字段含义为服务器通过什么字段获取回调函数的名称
        jsonp: 'callback',
        // 声明本地回调函数的名称,jquery 默认随机生成一个函数名称
        jsonpCallback: 'jsonpCallback',
        success: function (data) {
          console.log("ajax success callback: " + data.name);
        },
        error: function (jqXHR, textStatus, errorThrown) {
          console.log('error ' + textStatus + ' ' + errorThrown);
        }
      });
    });
    

    服务器 3001 上对应的处理函数为:

    app.post('/ajax/deal', function (req, res) {
      /* req.body.name -> req.query.name 
      jsonp实质为script的src链接: 
      /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1 */
      console.log('server accept', req.query.name, req.query.id);
      var data = "{" + "name:'" + req.query.name + " - server 3001 process'," + "id:'" + req.query.id + " - server 3001 process'" + "}"
      var callback = req.query.callback
      var jsonp = callback + '(' + data + ')'
      console.log(jsonp)
      res.send(jsonp)
      res.end()
    })
    

    这里一定要注意 data 中字符串拼接,不能直接将 JSON 格式的 data 直接传给回调函数,否则会发生编译错误: parsererror Error: jsonpCallback was not called。

    其实脑海里应该有一个概念:利用 JSONP 格式返回的值一段要立即执行的 JavaScript 代码,所以不会像 ajax 的 XmlHttpRequest 那样可以监听不同事件对数据进行不同处理。

    处理结果如下所示:

    ajaximage6.png

    2. 使用 script 标签原生实现 JSONP

    由于实现的原理不同,由 JSONP 实现的跨域调用不是通过 XmlHttpRequset 对象,而是通过 script 标签,所以在实现原理上,JSONP 和 Ajax 已经一点关系都没有了。看上去形式相似只是由于 jQuery 对 JSONP 做了封装和转换。

    比如在上面的例子中,我们假设要传输的数据 data 格式如下:

    {
      name: "chiaki",
      id": "3001"
    }
    

    那么数据是如何传输的呢?HTTP 请求头的第一行如下:

    GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1
    

    可见,即使形式上是用 POST 传输一个 JSON 格式的数据,其实发送请求时还是转换成 GET 请求。

    其实如果理解 JSONP 的原理的话就不难理解为什么只能使用 GET 请求方法了。由于是通过 script 标签进行请求,所以上述传输过程根本上是以下的形式:

    <script src = 'http://localhost:3001/ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032'></script>
    

    这样从服务器返回的代码就可以直接在这个 script 标签中运行了。下面我们自己实现一个 JSONP:

    服务器 3000请求页面的 JavaScript 代码中,只有回调函数 jsonpCallback:

    function jsonpCallback(data) {
      console.log("jsonpCallback: "+data.name)
    }
    

    服务器 3000请求页面还包含一个 script 标签:

    <script src='http://localhost:3001/jsonServerResponse?jsonp=jsonpCallback'></script>
    

    服务器 3001上对应的处理函数:

    app.post('/jsonServerResponse', function (req, res) {
      var cb = req.query.jsonp;
      console.log(cb);
      var data = 'var data = {' + 'name: $("#name").val() + " - server 3001 jsonp process",' + 'id: $("#id").val() + " - server 3001 jsonp process"' + '};'
      var debug = 'console.log(data);'
      var callback = '$("#submit").click(function() {' + data + cb + '(data);' + debug + '});'
      res.send(callback)
      res.end()
    })
    

    与上面一样,我们在所获取的参数后面加上 “ – server 3001 jsonp process” 代表服务器对数据的操作。从代码中我么可以看到,处理函数除了根据参数做相应的处理,更多的也是进行字符串的拼接。

    最终的结果为:

    ajaximage4.png

    JSONP 总结

    至此,我们了解了 JSONP 的原理以及实现方式,它帮我们实现前端跨域请求,但是在实践的过程中,我们还是可以发现它的不足:

    • 只能使用 GET 方法发起请求,这是由于 script 标签自身的限制决定的。
    • 不能很好的发现错误,并进行处理。与 Ajax 对比,由于不是通过 XmlHttpRequest 进行传输,所以不能注册 success、 error 等事件监听函数。

    使用 CORS 实现跨域调用

    Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。

    CORS 的实现

    服务器 3000 上的请求页面 JavaScript 不变,如下:

    $(function() {
      $("#submit").click(function() {
        var data = {
          name: $("#name").val(),
          id: $("#id").val()
        };
        $.ajax({
          type: 'POST',
          data: data,
          url: 'http://localhost:3001/cors',
          dataType: 'json',
          cache: false,
          timeout: 5000,
          success: function(data) {
              console.log(data)
          },
          error: function(jqXHR, textStatus, errorThrown) {
              console.log('error ' + textStatus + ' ' + errorThrown);
          }
        });
      });
    });
    

    服务器 3001上对应的处理函数:

    app.post('/cors', function(req, res) {
      res.header("Access-Control-Allow-Origin", "*");
      res.header("Access-Control-Allow-Headers", "X-Requested-With");
      res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
      res.header("X-Powered-By", ' 3.2.1')
      res.header("Content-Type", "application/json;charset=utf-8");
      var data = {
          name: req.body.name + ' - server 3001 cors process',
          id: req.body.id + ' - server 3001 cors process'
      }
      console.log(data)
      res.send(data)
      res.end()
    })
    

    在服务器中对返回信息的请求头进行了设置。

    最终的结果为:

    ajaximage5.png

    CORS 与 JSONP 的对比

    • CORS 除了 GET 方法外,也支持其它的 HTTP 请求方法如 POST、 PUT 等。
    • CORS 可以使用 XmlHttpRequest 进行传输,所以它的错误处理方式比 JSONP 好。
    • JSONP 可以在不支持 CORS 的老旧浏览器上运作。

    一些其它的跨域调用方式

    window.name

    window.postMessage()

    相关文章

      网友评论

        本文标题:前端跨域请求方法整理

        本文链接:https://www.haomeiwen.com/subject/dtczxxtx.html