jsonp原理

首先我们要知道跨域是什么,又为什么要有跨域操作

跨域是什么

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

好这里我们提炼出两个东西---1.同源策略:url首部的协议+域名+端口号必须一样,一者不同即为跨域

                                              2.安全限制(这也是为什么需要跨域操作下一段介绍)

为什么需要跨域操作/为啥需要同源策略/为什么要限制ajax跨域(面试可能会这样问)

这个和cookie的存储原理有关(这里不介绍cookie),在生活里我们登录一个网站P(photoshop学习网站),然后他会存个通行证,下一次我们再上p站的时候

它是不是就自动登录了,我们用别的网站去请求登录p是不是就登录不上。

然后这个例子我们把它专业化一下下

1.客户向p网站的服务器发送登录请求,携带账号密码数据

2.P网站的服务器校验账号密码正确后,返回响应并给本地添加了Cookie

3.之后客户再次向P网站发起请求会自动带上P网站存储在本地的cookie

4.P网站的服务器从cookie中获取账号密码数据后,返回登陆成功界面。

假如ajax请求可以跨域,那我是不是可以在这个博客里写一段js,使用ajax向你的学习网站发起登录请求,因为

很多人的电脑上会存有学习网站的cookie不需要输入账号密码直接就自动登录了,再ajax回调函数中解析了返回的数据,

我就能知道你学的是不是potoshop了。

所以才需要同源策略,为了用户的信息安全也必须限制ajax的跨域操作

ps:同源策略限制内容:(1)存储内容:cookie,localStorage,sessionStorage,(2)DOM节点

                                     (3)ajax请求,需注意ajax请求会发送出去,也会被返回,但会被浏览器拦截。

不被限制的有img  link  script

进入正题jsonp

 我们现在已经知道了能够不被同源限制的标签有img link script 那我们该选择哪个嘞?

当然是script,只有这兄弟和代码有关嘛

首先咱得整个服务器出来用 node模拟一下

//服务端
const http =require("http");//导入http模块
http.createServer(
    (req,res) =>
    {
      let weather="四川 梅雨天";//客户端要访问的数据
      res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
      });//防止乱码,用jq的Ajax就不需要,Ajax会自动识别
      res.write(weather);
      res.end();
})
.listen(3000);//3000端口

然后在客户端写<script src="http://localhost:3000",会得到一个类型错误,因为js没法解析四川 梅雨天呀,这玩意儿都不是个语句,它笨解析不了

也就是说我们要在res.write拼接一条语句传给客户端于是乎

//服务端
const http =require("http");
http.createServer(
    (req,res) =>
    {
      let weather="四川 梅雨天";
      res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
      });
      res.write(`("document.write(${weather}")`);
      res.end();
})
.listen(3000);

然后再在客户端写<script src="http://localhost:3000",就发现页面上能打印出四川 梅雨天了,可是有个问题,我们的操作是不是写死了?那咱就在

客户端整个操作有关的函数呗

  function  show(w) {
   alert(w);
  }
</script>

然后把服务器的res.write(`("document.write(${weather}")`);改成res.write(`("show(${weather}")`);嗯~ o(* ̄▽ ̄*)o可是又有问题了我要是想改函数是不是要改两个地方

可不可以把这个函数整成动态的?可以的我们把函数的信息放到url的query里

<script src="http://localhost:3000?callback=show">
/*这里用callback接受函数信息,又因为本来show就是回调函数(相信大家体会出来了)所以用的callback命名,爱用啥都可以*/

那么在服务器咋个取得callbck嘞

onst http =require("http");
const url=require("url");//引入url模块
http.createServer(
    (req,res) =>
    {
      var Url=url.parse(req.url,true);
      var callback=Url.query.callback;//大概意思从url取到callback
      let weather="四川 梅雨天";
      res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
      });
      res.write(`${callback}("${weather}")`);
      res.end();
    })
    .listen(3000);

然后这个问题也解决了,但我们平时点一个按钮然后做跨域请求肯定不是这样把script标签写在外面浪费内存不说,代码还不美观

所以终极的代码来了

//客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<button>天气预报</button>
<script>
  let click=document.querySelector("button");
  click.addEventListener("click",()=>{
    let script=document.createElement("script");
    script.src=`http://localhost:3000/?callback=show`;
    document.body.appendChild(script)
  });//对按钮添加点击事件在body里生产script
  function  show(w) {
    alert(w);
    document.body.lastChild.remove();//对每次生成的script在结束后移除
  }
</script>
</body>
</html>
//服务器代码不变

好了以上就是jsonp的原理总结一下,(1)声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,

                                                                    函数形参为要获取目标数据(服务器返回的data)。

                                                           (2)创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,

                                                                    还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。

                                                           (3)服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,

                                                           (4)最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),

                                                                     对返回的数据进行操作

所以jsonp是一种思想每个人写的jsonp可能都会不一样,但终终极方案JQubiery里ajax请求里写 dataType : "jsonp",完事儿了,但思想还是要理解的。

 

原文地址:https://www.cnblogs.com/jiangxiaoming/p/13657929.html