Skip to content
On this page

jsonp 解决跨域请求原理

借助 script 标记发送跨域请求

为什么会出现跨域请求:不同源地址之间默认是不能互相通过 ajax 跨域请求成功的,这就是同源策略,请求可以发送出去,但是浏览器出于安全机制不允许拿到响应内容。

如何才算跨域请求成功: 1.能发送出去; 2.能接收回来。 平时发送请求的方式有哪些:img link script iframe 等。

javascript
// ## 1. img
// 可以发送不同源地址之间的请求
// 无法拿到响应结果
// var img = new Image();
// img.src = 'http://localhost:8080/AJAX/27data.php';

// ## 2. link
// 可以发送不同源地址之间的请求
// 无法拿到响应结果
// var link = document.createElement('link');
// link.rel = 'stylesheet';
// link.href = 'http://localhost:8080/AJAX/27data.php';
// document.body.appendChild(link);

// ## 3. script
// 可以发送不同源地址之间的请求
// 无法拿到响应结果(借助script标签能够自动执行响应回来的js代码拿到响应结果)
var script = document.createElement("script");
script.src = "http://localhost:8080/AJAX/28time.php";
document.body.appendChild(script); // 开始发起请求,这里也相当于异步的方式发送请求
// 相当于请求的回调
function foo(res) {
  console.log(res);
}

27data.php 内容如下

php
<?php
header('Content-Type:application/json');
$data = array(
    array(
      'id' => 1,
      'name' => '张三',
      'age' => 18
    ),
    array(
      'id' => 2,
      'name' => '李四',
      'age' => 20
    ),
    array(
      'id' => 3,
      'name' => '二傻子',
      'age' => 18
    ),
    array(
      'id' => 4,
      'name' => '三愣子',
      'age' => 19
    )
  );
  $json = json_encode($data);
 echo $json;

28time.php 内容如下

php
<?php
header('Content-Type: application/javascript');
$json = json_encode(array(
  'time' => time()
));
// 在 JSON 格式的字符串外面包裹了一个函数的调用,返回的结果就变成了一段 JS 代码
echo "foo({$json})";

jsonp 原理

利用 script 标签能发送跨域请求,原理是服务端将响应的数据包裹进一个函数的调用,那么响应回去的东西相当于是一段 js 代码,而且 script 标签会自动执行该响应回来的 js 代码,只要客户端提前声明了服务端包裹数据的那个函数,就能获取到里面的数据了。

jsonp 封装

javascript
function jsonp(url, params, callback) {
  //生成随机的一个函数名
  var funcName = "jsonp_" + Date.now() + Math.random().toString().substr(2, 5);
  //判断传入的参数是不是对象,是就变成url传参形式
  if (typeof params === "object") {
    var tempArr = [];
    for (var key in params) {
      var value = params[key];
      tempArr.push(key + "=" + value);
    }
    params = tempArr.join("&");
  }
  //增加一个script标签
  var script = document.createElement("script");
  script.src = url + "?" + params + "&callback=" + funcName;
  document.body.appendChild(script);
  //函数声明,调用是在传回来的js脚本执行时调用,该函数调用之后,php执行后的数据就是该函数的实参
  window[funcName] = function (data) {
    //回调函数的调用,该函数里主要是获取到数据之后,用来干嘛
    callback(data);
    //删除这个全局的方法
    delete window[funcName];
    //从文档中删除这个标签
    document.body.removeChild(script);
  };
}

//jsonp函数调用
jsonp(
  "http://localhost:8080/AJAX/30server.php",
  {
    id: 123,
  },
  function (res) {
    console.log(res);
  }
);

30server.php 内容如下

php
<?php
$conn = mysqli_connect( 'localhost', 'root', '', 'ajax_jsonp_test' );

$query = mysqli_query( $conn, 'select * from users' );

while ( $row = mysqli_fetch_assoc( $query ) ) {
    $data[] = $row;
}

if ( empty( $_GET['callback'] ) ) {
    header( 'Content-Type: application/json' );
    echo json_encode( $data );
    exit();
}

// 如果客户端采用的是 script 标记对我发送的请求
// 一定要返回一段 JavaScript
header( 'Content-Type: application/javascript' );
$result = json_encode( $data );

$callback_name = $_GET['callback'];

echo "typeof {$callback_name} === 'function' && {$callback_name}({$result})";