前端请求之跨域解决方案

跨域

什么是跨域?

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交2.) 资源嵌入: 、

参考链接:前端常见跨域解决方案(全)

1. 什么是同源策略?

同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种 A 源访问 B 源的资源的通信称为跨域;

示例:

let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com/', true);
xhr.onreadystatechange = function () {
	if (xhr.readyState === 4 && xhr.status === 200) {
		console.log('xxx')
	}
};
xhr.send();
  • 以上请求会报错:
Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://localhost:63342'
 has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header 
 is present on the requested resource.

当出现以上错误时说明你正在进行一个跨域的操作;

2. 同源策略的要求:

同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;

3. 常用的跨域解决方案:

  1. 通过jsonp跨域
  2. 跨域资源共享(CORS): Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
  3. postMessage跨域
  4. nginx代理跨域,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
  5. window.name + iframe跨域
  6. WebSocket协议跨域
  7. document.domain + iframe跨域 //document.domain存放的是载入文档的服务器的主机名
  8. location.hash + iframe
  9. nodejs中间件代理跨域
  10. location.hash + iframe
  11. 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
  • ajax 不能进行非同源的请求

1. JSONP跨域

JSONP 是一种常用的解决跨域的方式;
原理:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据

  • JSONP 跨域原理: 利用了script的src属性是非同源策略的,就可以获取到非同源的数据,

然后当数据请求成功,再调用callback的回调函数;会把数据传递给这个回调函数的第一个参数;

  • JSONP : 需要后端配合
  • JSONP: 只能发送get请求,不能发送post请求;
  1. 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
  2. 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
  3. 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
  4. fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据

JS代码:

function fn(data) {
	console.log(data);
}

HTML代码

<script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>

简述

    <script src="留言板/jquery-3.1.1.min.js"></script>
    <script>
        // 跨域: 是由于浏览器自身的同源策略导致的;浏览器只能访问同域下的数据或内容;
        // 同域: 要求协议相同 域名相同  端口号相同;如果其中一个不相同,就会跨域;

        // http://www.baidu.com:443/example/src/1.html
        // http://www.baidu.com:888
        // 只能访问该端口下的项目或者是数据;
        // 浏览器: 同源策略;同源策略阻止一个域的脚本去请求另一个域的脚本进行交互;

        // 前端都是通过ajax来获取数据的;
        // ajax 不能访问不同源下的数据或内容
        // $.ajax({
        //     // url是相对路径;相对于html文件
        //     // url:"http://127.0.0.1:5501/6.正式课第六周/day4/3.txt",// 
        //     url:"http://matchweb.sports.qq.com/kbs/calendar?columnId=100000",
        //     type:"get",
        //     success:function(val){
        //         console.log(val);
                
        //     }
        // })

        // script   : script 有个src属性,这个src属性是非同源的;可以支持跨域请求
        function fn(data){
            console.log(data);
        }

        // JSONP 跨域原理: 利用了script的src属性是非同源策略的,就可以获取到非同源的数据,
然后当数据请求成功,再调用callback的回调函数;会把数据传递给这个回调函数的第一个参数;

        // JSONP : 需要后端配合
        // JSONP: 只能发送get请求,不能发送post请求;
        // cors  window.name  webSocket document.domain  nginx
    </script>
    <script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn">
      </script>

jsonp封装

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <script>
        // 跨域: 同源和非同源;
        // 项目工作场景:官网:8000   管理系统:9000 
        // 同源和非同源 : 
        // 所有的请求的url都是这个样子
        // http://127.0.0.1:8080/app/index.html
        // 协议   域名      端口号
        // 因为axios.
        //axios.get("/user/list").then();
        //axios.post("/customer/add".then())

        // ajax 不能进行非同源的请求
        // script的src是非同源的,可以从一个项目访问另一个项目,当数据成功返回之后,
      会默认调用跨域地址后面的callback 回调函数
        // 只能发送get请求;
        // function fn(data){
        //     // data就接收到了后端的数据
        // }
        // // 
        // // jquery
        // $.ajax({
        //     url:"user/login",
        //     dataType:"jsonp",
        //     success:function(data){

        //     }
        // })
        function jsonp(options) {
            return new Promise(function (resolve, reject) {
                window[options.cb] = function (data) {
                    resolve(data);
                    document.body.removeChild(script);
                }
                let script = document.createElement("script");
                let str = `${options.url}?`;
                for (let key in options.params) {
                    str += key + "=" + options.params[key] + "&"
                }
                str += "cb=" + options.cb;
                script.src = str;
                document.body.appendChild(script);
            })
        }
        jsonp({
            url: "/login",
            params: {
                user: 11223
            },
            cb: "fn"
        }).then(function (data) {
            // data就是成功获取的数据

        })
    </script>
    <script src="http://www.baidu.com?callback=fn"></script>
</body>

</html>

2.cors跨域

  • 跨域资源共享(CORS): Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;

1.server.js

let express = require('express');
let app = express();
//利用cors解决跨域
app.use(function(req,res,next){
    //设置允许的请求地址  * 代表所有的路径
    res.header('Access-Control-Allow-Origin','*');
    //设置允许的跨域方式
    // res.header('Access-Control-Allow-Origin','GET,POST')
    next()
})
//路由
app.get('/getData',function(req,res){
    res.send('你很帅')
})
app.listen(8080);
console.log("成功");

2.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script src='../../node_modules/axios/dist/axios.min.js'></script>
    <script>
         // 从5501向8080发送请求,被阻断了,跨域;
        axios.get("http://127.0.0.1:8080/getData").then(function (data) {
            console.log(data);
            
        })
    </script>
</body>

</html>

3.postMessage跨域

1.server.js

let express = require('express');
let app = express();
//解析静态资源文件
app.use(express.static(__dirname))
app.listen(8080,function(){
    console.log('启动成功');
  
    
})

2.index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <iframe src="http://127.0.0.1:8080/message.html" id="frm"></iframe>
    <input type="button" value="ok" onclick = 'run()'>
    <script>
        function run(){
            let frm = document.getElementById('frm');
            frm.contentWindow.postMessage({name:'快乐和滑稽'},'http://127.0.0.1:8080')
            console.log(2);
            
        }
    </script>
</body>
</html>

3.message.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        //接收 
        window.addEventListener('message',function(e){
            console.log(e.data);
            
        })
    </script>
</body>
</html>

demo–利用iframe嵌套父子窗口通信

父窗口:

<!--我是父窗口-->  
<div class="parent" >
      <iframe src="子窗口链接" id="iframe"></iframe>
</div>
<script>
//监听子窗口信息
 window.addEventListener('message',function(event){
   ...
   })
//父窗口给子窗口发消息,
document.getElementByID('iframe').contentWindow.postMessage(msg,'子窗口源');
   
</script>

子窗口

<!--我是子窗口-->  
<div class="child"></div>
<script>
//子窗口给父窗口发消息
try {//放到trycatch里面,解决有些手机卡住报错问题
  window.top.postMessage(msg,'父窗口源');
      //嵌套一层使用window.top(parent),多层window.frameElement
      //使用top而不是window,top指向iframe最顶层窗口
  } catch (error) {
}
//监听父窗口信息
 window.addEventListener('message',function(event){
   ...
   })
</script>

注意:
父窗口给子窗口发信息,需要用iframe的contentWindow属性作为调用主体
子窗口给父窗口发的信息需要使用window.top,多层iframe使用window.frameElement

4. Nodejs中间件代理跨域

  • webpack.config.js部分配置:
module.exports = { 
entry: {},
 module: {}, 
... 
devServer: { 
historyApiFallback: true,
 proxy: [{
 context: '/login', 
target: 'http://www.domain2.com:8080', // 代理跨域目标接口 
changeOrigin: true, 
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
 }], 
noInfo: true 
}}

iframe

  • iframe :标签 可以在父页面中嵌套子页面
  • iframe :src的地址就是嵌套子页面的地址
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        html,
        body {
            height: 100%;
        }

        iframe {
            border: none;
            width: 100%;
            height: 95%;
        }
    </style>
</head>

<body>
    <!-- <iframe src="3.child.html"></iframe> -->//chilid子页面
    <a href="https://baidu.com" target="iframeA">百度</a>
    <a href="https://taobao.com" target="iframeA">淘宝</a>
    <a href="https://jd.com" target="iframeA">京东</a>
    <iframe src="https://baidu.com" name="iframeA"></iframe>

    <script>
        // iframe :标签  可以在父页面中嵌套子页面
        // iframe :src的地址就是嵌套子页面的地址
    </script>
</body>

</html>
align
- left
- right
- top
- middle
- bottom
不赞成使用。请使用样式代替。
规定如何根据周围的元素来对齐此框架。
frameborder
- 1
- 0
规定是否显示框架周围的边框。
height
- pixels
- %
规定 iframe 的高度。
longdesc URL 规定一个页面,该页面包含了有关 iframe 的较长描述。
marginheight pixels 定义 iframe 的顶部和底部的边距。
marginwidth pixels 定义 iframe 的左侧和右侧的边距。
name frame_name 规定 iframe 的名称。
sandbox
- “”
- allow-forms
- allow-same-origin
- allow-scripts
- allow-top-navigation
启用一系列对 中内容的额外限制。
scrolling
- yes
- no
- auto
规定是否在 iframe 中显示滚动条。
seamless seamless 规定 看上去像是包含文档的一部分。
src URL 规定在 iframe 中显示的文档的 URL。
srcdoc HTML_code 规定在 中显示的页面的 HTML 内容。
width
- pixels
- %
定义 iframe 的宽度。
<iframe width="160" height="180" frameborder="0" scrolling="no" 
src="//chongzhi.jd.com/jdhome-czindex-2017.html">
  </iframe>

猜你喜欢

转载自blog.csdn.net/Sheng_zhenzhen/article/details/106742764