跨域以及解决跨域请求报错

330 阅读7分钟

跨域

浏览器为了用户的信息安全,网页中有一个网络请求技术是AJAX技术在做网络请求时请求的网址和当前页面的网址不是同一台服务器,就会被拒绝接收服务器发送的数据包

AJAX技术做网络请求时会受浏览器同源策略的影响就不允许跨域通信,会出现跨域问题

跨域错误信息

image.png

浏览器对跨域请求的拦截

浏览器允许发起跨域请求,后端服务器能收到请求且也会返回请求的数据到客户端但是跨域请求回来的数据会被浏览器拦截,无法被页面获取到

image.png

同源策略

同源策略是浏览器的一种安全功能,同源策略会阻止一个域的javascript脚本和另外一个域的资源文件进行交互。

同源指在同一个域,即两个网址的协议,ip ,port,三者一样代表同源

1.页面网址的pathname不参与同源判断,以下两个网址是同源的

https://www.xxx.com/index.html       

https://www.xxx.com/home/goods/ajax1

2.以下两个网址是异源的,协议不一样

http://www.xxx.com/index.html       

https://www.xxx.com/ajax

3.以下两个网址是异源的,域名(ip:port)不一样

http://www.xxx.com/index.html       

http://www.xxx.cn/ajax

4.以下两个网址是同域,域名(ip:port)一样

http://www.xxx.com/index.html //假设DNS解析为 172.153.2.60:7001

http://172.153.2.60:7001/ajax

域名结构:mail.cctv.com(从左到右依次是三级域名.二级域名.顶级域名),域名会被DNS解析为ip+port,上面两个网址是同源但是在跨域问题上协议和端口造成的跨域问题是前端无法解决的,它不会根据域名对应的IP地址和port端口号是否相同来判断

解决跨域问题,实现跨域数据请求

CORS

CORS是W3C标准,属于跨域AJAX请求的根本解决方案,浏览器可以根据响应头的Access-Control-Allow-Origin字段的值来判断是否有权限获取数据

一般情况下,后端服务器如果没有在这个字段做特殊处理的话,跨域是没有权限访问的,所以响应数据被浏览器给拦截了,在AJAX回调函数里是获取不到数据的

CORS实现跨域请求

只需要在后端服务器设置,给要请求的数据所在的后端服务器添加响应头Access-Control-Allow-Origin,星号表示所有域名都可以跨域请求到数据,常用于项目开发阶段。也可用指定域名代替星号,表示只有指定的域名可以跨域请求到数据,常用于项目中的二级服务器跨域数据共享

//指定域名,只能指定的域名跨域请求
res.setHeader("Access-Control-Allow-Origin","http://localhost:8081");

//星号表示所有域名都可以跨域请求
res.setHeader("Access-Control-Allow-Origin","*");

这台后端服务器只提供一个有数据的网址接口

let http=require("http");
let router=require("./router.js");
http.createServer(router).listen(8080);
router.get("/AOTU",function(req,res){
    //CROS关键代码,设置后端服务器响应头Access-Control-Allow-Origin
    res.setHeader("Access-Control-Allow-Origin","http://localhost:8081");
    res.end('{"name":"AOTU","leader":"sy jung"}');
});

这台服务器的前端项目用AJAX技术请求另外一台后端服务器的数据,这两台后端服务器端口不一样,是不同源的

后端服务器代码

let http=require("http");
let router=require("./router.js");
http.createServer(router).listen(8081);

前端项目网页代码

<body>
    <style>
        .box p {
            margin-bottom: 10px;
            color: gold;
            margin-left: 20px;
        }

        .btn {
            background-color: goldenrod;
            border-radius: 20px;
            margin-left: 20px;
            cursor: pointer;
        }
    </style>
    <div class="box">
        <p>如果你想得到数据,请点击下面的按钮</p>
        <buttton class="btn" onclick="fn()">点我try try,可以跨域请求</buttton>
    </div>
    <div class="text"></div>
    <script>
        function fn() {
            let text = document.querySelector(".text")
            let xhr = new XMLHttpRequest() || new ActiveXObject("MicrosoftXMLHTTP");
            xhr.open("GET", "http://localhost:8080/AOTU", true);
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    let obj = JSON.parse(xhr.responseText);
                    let p = document.createElement("p");
                    p.innerHTML = obj.name;
                    let p1 = document.createElement("p");
                    p1.innerHTML = obj.leader;
                    text.appendChild(p);
                    text.appendChild(p1);
                };
            };
        };
    </script>
</body>

2.gif

JSONP

前端网页中用ajax请求跨域服务器的网址会报跨域错误但是用html的script标签,img标签,a标签,可以请求第三方的资源,不受同源策略影响。

JSONP技术实现跨域请求的前提必须后端工程师配合,写出JSONP数据需要后端将JSON数据拼接为有函数调用的形式,前端才能用script标签请求的到数据。否则后端发送的JSON数据,在前端收到的这个JSON字符串数据就会用v8引擎去运行,运行的结果就仅仅是将这个JSON字符串数据在内存中被创建成为了一个对象,但没有变量来接收这个对象,前端就无法操作这个对象。

JSONP(JSON with Padding)是JSON数据的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题,其核心思想是利用写js代码脚本script标签里面的src属性进行跨域数据访问

script标签的src属性去请求跨域服务器的网址不会报跨域错误,但是实际执行的时候通过src属性会获得一段字符串,因为后端服务器返回的数据包不是字符串就是二进制流的buffer文件编码其实质就是一段字符串,这段返回的字符串必须是符合js语法的,请求完毕以后会直接把请求过来的编码用v8引擎去运行

JSONP实现跨域请求

1.前端在发送请求的时候, 前端可以通过script标签的src属性发起一个请求,把前端提前创建的函数名以query参数的形式放到这个要请求的数据的网址中发送给后端服务器

2.后端服务器解析了query参数以后在服务器中获取数据资源,根据接收的函数名拼接为函数调用的形式并返回到前端,前端将获取的返回数据按js语法进行解析,前端就顺利的获取到数据了

这一台服务器只提供一个有数据的网址接口

let http=require("http");
let router=require("./router.js");
let url=require("url");
let querystring=require("querystring");
http.createServer(router).listen(8089);
router.get("/sy",function(req,res){
    let obj={name:"tara",member:"BQSEHJ"};
    let str=JSON.stringify(obj);
    //query1解析出来是前端发来的参数:"callback=fm&key2=value2"
    let query1=url.parse(req.url).query;
    //将前端发来的参数字符串"callback=fm&key2=value2"转换为对象
    let querobj=querystring.parse(query1);
    //取出对象的属性值就是接收的函数名拼接为函数调用的形式并返回到前端
    res.end(`${querobj.callback}(${str})`)
});

这台服务器的前端项目用JSONP技术请求另外一台后端服务器的数据,这两台后端服务器端口不一样,是不同源的

后端服务器代码

let http=require("http");
let router=require("./router.js");
http.createServer(router).listen(8086);

前端项目网页代码

<body>
    <div class="box">
        <p>点击下方按钮try try</p>
        <button class="btn" onclick="fn()">点击我实现JSONP请求</button>
    </div>
    <div class="text"></div>
    <script>
        let text=document.querySelector(".text");
        function fn() {
            //变量名拼接时间的毫秒数可以让变量名是独一无二的
            let fm="name"+new Date().getTime()
            //前端提前创建的函数名
            window[fm] = function (arg) {
                let p = document.createElement("p");
                p.innerHTML = arg.name;
                let p1 = document.createElement("p");
                p1.innerHTML = arg.member;
                text.appendChild(p);
                text.appendChild(p1);
            };
            let script1=document.createElement("script");
            //相当于将跨域请求的网络数据作为创建的fm函数的实参传给形参并调用fm函数
            //代码等价于fm('{"name":"tara","member":"BQSEHJ"}')
            //提前创建的函数名以query参数的形式放到这个要请求的数据的网址中发送给后端服务器
            script1.src=`http://localhost:8089/sy?callback=${fm}`;
            document.body.appendChild(script1);
        };
    </script>
</body>

image.png

Proxy代理

产生跨域的原因就是浏览器的同源政策是针对于AJAX的请求,并不会限制服务器之间的跨域交互,比如A服务器的前端项目请求B服务器,可以让代理C服务器帮助请求数据,而这个代理C服务器正是和A服务器的前端项目是相同域名的,只需去用代理服务器去发请求再去接收数据从而达到跨域。后端的网络请求工具,它可以请求任何一台跨域的后端服务器。即网页的服务器先跨域请求不同服务器的数据,同时负责转发跨域请求到的数据,网页只从网页所在的服务器请求数据,那么网页和数据来自同一服务器,就不是跨域了。

03D6843D82DC06534681978A222B7F19.png

自己的服务器代码,用后端服务器请求工具跨域请求另外一台服务器的资源

let http=require("http");
let router=require("./router.js");
http.createServer(router).listen(8087);
//一个后端的网络请求工具,它可以请求任何一台跨域的后端服务器
let request=require("request");
router.get("/baidu",function(req,res){
    //回调函数的调用是在要请求的数据所在的后端服务器返回了请求的数据时才会调用
    request("https://www.baidu.com",function(err,res,data){
        res.end(data);
    });
});

自己服务器的前端网页代码,用AJAX技术请求自己后端服务器的数据资源

<body>
    <h1>句号</h1>
    <button onclick="fn()">点击我tr try,你就可以去到百度首页页面</button>
    <script>
        function fn() {
            let xhr = new XMLHttpRequest() || new ActiveXObject("MicrosoftXMLHTTP");
            xhr.open("GET", "http://localhost:8087/baidu", true);
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    document.body.innerHTML=xhr.responseText;
                };
            };
        };
    </script>
</body>

2.gif

Node.js发送请求,需要用到request这个模块,是第三方模块,使用前需要先引入到项目中。

request在发送Get请求时:

第1个参数:要请求的完整URL,包括参数

第2个参数:请求结果回调函数,回调函数会传入3个参数,第1个包含错误数据的对象,第2个响应对象,第3个请求的数据