7-6 前后分离(跨域实现:CORS、JSONP)

  • 请问怎么跨域?
  • 注意:无论是CORS还是JSONP都是要双方主动沟通,也就是对方必须要写一个头文件或者添加一个JS文件,没有这些还是不能访问,不能去抢数据。

解法一:CORS

  • 问题根源
    1.浏览器默认不同源之间不能相互访问数据
    2.但qq.com和lzy.com其实都是我的网站
    3.我就是想要两个网站相互访问,浏览器为什么阻止
  • 解决问题:用CORS
    1.浏览器说,如果要共享数据,需要提前声明
    2.浏览器说,qq.com在响应头里写lzy.com可以访问
    3.具体语法:Access-Control-Allow-Origin:htttp://foo.example
    4.详情都在文档里:MDN文档
  • 具体做法:
    1.在qq-com目录中,server.js文件里的friends路径中设置一个响应头
    response.setHeader("Access-Control-Allow-Origin", "http://lzy.com:9999");
    2.意思是friends.json允许这个网站端口访问
    在这里插入图片描述
  • 允许多个网站访问
    request.headers['referer']
    referer可以读取是哪个网站访问json文件,然后直接通过referer把访问的地址写到允许的地方即可
  • 注意:CORS分为简单请求和复杂请求,具体看文档
  • IE 6789都不支持CORS,只有采用JSONP

解法二:JSONP

1.定义

  • JSONP和JSON没有任何关系
    在发明的时候是为了请求JSON数据,但其实不限于访问JSON数据,还有xml等都可以,因此和JSON没有特别严谨的关系
  • 问:没有CORS怎么跨域?
    答:虽然不能访问 .json文件,但我们可以访问任意网站的 .js文件
  • 问:JS又不是数据
    答:把数据写到JS文件里面就行了

2.步骤

  • lzy.com 访问 qq.com
  • qq.com 将数据(data)写到 /friends.js文件中
    1.不用一开始就将数据写进去,先写一个占位符{{data}},然后后台server.js将其替换(replace)掉即可。
    2.JS文件内容:window.xxx = {{data}}
    ——因为替换过来的JSON文件字符串不符合JS文件的语法
    ——因此采用赋值就符合JS文件的语法了
if (path === "/friends.js") {
  //其他代码略
  const string = fs.readFileSync("./public/friends.js").toString(); 
  const data = fs.readFileSync("./public/friends.json").toString();
  const string2 = string.replace("{{data}}", data); 
  response.write(string2);
  response.end()
}
  • lzy.com 用 script 标签引用 /friends.js
    1.使用JS动态引用的
    2.每请求一次,就会产生一个 script标签,多次以后就会导致页面很臃肿
    ——优化:请求玩之后就删掉script标签
const script = document.createElement('script')
script.src = 'http://qq.com:8888/friends.js'
script.onload=()=>{//监听script标签的onload事件
  console.log(window.xxx)
  script.remove()
}
document.body.appendChild(script)
  • 获取数据:window.xxx
    因为请求过来的JS文件会执行,也就是把数据写到了 window.xxx 上面
  • 关于获取的JS文件
    1.window.xxx是赋值:获取JS文件并执行,相当于是赋值
    2.window.xxx是函数:执行JS文件相当于是调用函数,因此要提前定义
//friends.js中是赋值
window.xxx = {{data}}
//沟通一下:在允许源这边就可以直接赋值使用
script.onload=()=>{
  console.log(window.xxx)
}

//friends.js是函数
window.xxx({{data}})
//允许源:这边就先定义一个函数准备好
window.xxx = (data)=>{
  console.log(data)
}
  • window.xxx 就是一个回调啊!!!
    定义了函数 window.xxx,却不调用它,等qq.com的一个JS脚本来调用,其结果就是调用的参数(这就是跨域名的回调)

3.referer

  • JSONP的话所有网站都能访问该JS文件里的数据内容了
    CORS可以指定谁能访问,但JSONP不能指定
  • 可以做referer检查
    1.request.headers['referer']
    2.如果检查到地址不是我们所允许访问的,就不让其访问(做个字符串匹配
    3.不是就给一个404
else if (path === "/friends.js") {
    if(request.headers['referer'].indexOf("http://lzy.com:9999")===0){
      response.statusCode = 200;
      response.setHeader("Content-Type", "text/json;charset=utf-8");
      const string = fs.readFileSync("./public/friends.js").toString(); //变成字符串
      const data = fs.readFileSync("./public/friends.json").toString();
      const string2 = string.replace("{{data}}", data); //替换JS文件中的内容为真正的数据
      response.write(string2);
      response.end();
    }else{
      response.statusCode = 404
      response.end()
    }
  • 但是一旦给了权限的 lzy.com 被攻陷了,那么 qq.com 也会很危险,还要再加强防御

4.优化

  • 函数window.xxx不写死,自动生成(random)
    1.这样就可以接收多个JS文件,且不会重名
    2.但访问的网站中JS并不知道我们的xxx叫什么,则使用查询参数传给 /friends.js
//名字随便怎么取,越复杂,越不会重名
const random = 'frankJSONPCallbackName'+ Math.random()
window[random] = (data)=>{
  console.log(data)
}
const script = document.createElement('script')
script.src = `http://.../friends.js?functionName=${random}`
document.body.appendChild(script)
  • 访问后台(serve.js)中获取到查询字符串
//friends.js
window['{{xxx}}']({{data}})
//serve.js
const string2 = string.replace("{{data}}", data).replace('{{xxx}}',query.functionName)
  • 这样随机的函数名就不会和别人冲突

5.封装(提高课)

  • 封装成jsonp('url').then(f1,f2)
    给一个url,成功就调f1,失败就调f2
  • 步骤:
    1.先定义一个函数jsonp
    2.创建一个Promise对象
    3.将用户传入的url当做jsonp函数的参数
    4.成功拿到data调用f1,失败则调用f2
  • JSONP比AJAX有一个天然的弱点,拿不到状态码,只知道成功或者失败
  • 封装完成后,只需要一句话即可请求JSONP
jsonp('http://qq.com:8888/friends.js')
.then((data)=>{
    console.log(data)//成功后调用函数console.log
})

6.functionName

  • JSONP约定,functionName统一叫callback
  • 后端的server.js中也统一叫callback,这是约定
script.src = `${url}?callback=${random}`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值