同源:

同源:ajax请求的url和网页所在的url具有相同的协议,域名,ip和端口

同源策略:为了数据安全,浏览器禁止通过ajax请求读取非同源的数据(注意:同源策略的两个关键点:1,浏览器 2,ajax请求)

同源策略下,浏览器会拦截跨域数据并报错,拦截的响应数据

跨域/跨源:ajax请求的url和网页所在的url具有不同的协议,域名,ip和端口

1,解决跨域请求报错的三种解决办法

1.1 COR头跨域

原理:在ajax请求的响应头中,添加cors字段,相当于令牌/通行证,可以避免浏览器拦截这个跨域数据(浏览器在接受ajax跨域数据时,会优先判断响应头中是否存在cors字段,如果有,则不拦截数据,如果没有,则报错)

步骤:在后端服务器接口响应之前,添加响应头


// 在响应头中添加cors字段, 允许这个接口跨域访问
 res.setHeader("Access-Control-Allow-Origin", "*")

//或

// 使用第三方cors模块允许跨域 (可一次设置所有接口跨域)
var cors = require("cors")
app.use(cors())


前端代码:

<h1 id="h1">这是主页</h1>
    <script>
        // 访问目标服务器的登录接口
        $.get("http://localhost:5000/login?name=张三", function(data){
            h1.innerText = data
        })
    </script>

  • 优点:简单方便,容易操作,前端请求无需修改ajax代码
  • 缺点:数据安全性降低,完全受后端限制,前端无法控制

1.2 jsonp跨域

原理:同源策略只对ajax请求有限制,非ajax请求无限制,非ajax请求(如:form表单请求、标签属性请求,a标签的href,img标签的src,script标签的src)不会被同源策略拦截,所以使用script标签的src属性发起的跨域请求不会被同源策略拦截。

步骤:前端(3种):

1,原生的jsonp请求:

前端:


<script>
        function myMethod(res){
            console.log(res)
            alert(res.name)
        }
    </script>
    <script src="http://192.168.124.43:3000/userInfo?callback=myMethod"></script>


  • callback这个字段不能自定义,是jsonp内部规定好的字段,不用callback调用不了

后端:


app.get('/userInfo', function(req, res) {
    console.log(req.query)
    req.query.callback({ name: "李四" })
})

2,常规的开发习惯写法

前端:


<script>
        function myMethod2(res){
            alert(res.name)
        }
        $("<script>").attr("src", "http://192.168.124.43:3000/userInfo?callback=myMethod2").appendTo("body")
    </script>


后端同1一样

3,jquery封装jsonp请求,使用$.getJSON()实现

前端:


<script>
        // 由于jquery封装后,回调函数是匿名函数, 所以参数写成  callback=?  ?代表后边匿名函数
        $.getJSON("http://192.168.124.43:3000/userInfo?callback=?", function(res) {
            alert(res.name)
        })
    </script>


后端:


app.get('/userInfo', function(req, res) {
    console.log(req.query)
     res.jsonp({ name: '李四' })
})


  • jsonp()函数内部会自动调用get请求参数种的callback字段传入的回调函数,jsonp()参数作为callback回调函数的参数返回响应
  • jsonp函数的作用有两个:1,调用参数种的callback回调函数 2,返回响应数据

优点:不降低数据安全性,不完全受后端控制,相比于代理服务器跨域更简单

缺点:比cors头跨域复杂,受客户端和服务器共同控制,原理较复杂

1.3 代理服务器 跨域

原理:同源策略只对浏览器发起的ajax请求有限制,非浏览器请求无限制,所以可以使用网页所在的同源服务器发起ajax跨域请求,获取跨域数据后返回给同源客户端

步骤:创建同源服务器,在服务器种添加代理

前端:


<h1 id="h1">这是主页</h1>
    <script>
        // 访问目标服务器的登录接口
        $.get("/myApi/login?name=张三", function(data) {
                h1.innerText += data
            })
            // 访问目标服务器的注册接口
        $.post("/myApi/register?name=张三", function(data) {
            h1.innerText += data
        })
    </script>


后端:

var proxy = require("http-proxy-middleware").createProxyMiddleware
app.use("/myApi", proxy({
    // 这个对象是代理的配置对象
    target: "http://localhost:5000",   //目标服务器地址
    changeOrigin: true,  // 修改请求路径
    pathRewrite: {       // 路径修改方案
        "^/myApi": ""
    }
}))

// http://localhost:3000/myApi/login?name=张三 
// 以上请求url代理后的url如下
// http://localhost:5000/login?name=张三

// http://localhost:3000/myApi/register?name=张三
// 以上请求url代理后的url如下
// http://localhost:5000/register?name=张三

  • 使用代理服务器跨域必须在前端服务器接口种下载并导入代理模块,并直接创建代理函数
  • 使用代理模块,调用代理函数,实现请求代理,这相当于创建了一个请求接口,同同源网页index.html访问,而参数"/myApi"就是同源请求的接口地址

2,四种ajax请求方式

2.1 原生的ajax请求方式

get请求方式


var xhr = new XMLHttpRequest();
        xhr.open("get", "http://open.douyucdn.cn/api/RoomApi/live?page=1")
        xhr.send()
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                console.log(1, JSON.parse(xhr.responseText))
            }
        }


post请求方式:


var xhr = new XMLHttpRequest()
        xhr.open("post", "http://open.douyucdn.cn/api/RoomApi/live")
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        xhr.send("page=1")
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                console.log(2, xhr.responseText);
            }
        }


  • 第一步:创建一个xhr对象 var xhr = new XMLHttpRequest();
  • 第二步:设置请求方式和请求URL xhr.open( ) 参数1为请求方式get/post,如果请求方式是get,则参数2的请求URL带传递参数;如果为post,则参数2的请求URL不带传递参数(即不带URL中的 ?后边的一系列参数)
  • 第三步:发送请求 xhr.send(); get请求,send不带参数;post请求,send参数是post请求的请求体, 也是键值对结构
  • 第四步:监听readystatechange ,判断readyState的请求准备状态是不是4;请求准备状态有1-4,1表示xhr创建;2表示数据处理完成;3表示请求已发送;4表示服务器返回结果,请求完成 responseText: "用户名已存在,注册失败"

2.2 jquery封装的ajax请求方式

$.get("http://open.douyucdn.cn/api/RoomApi/live", {
            page: 1
        }, function(data) {
            console.log(3.1, data);
        })

 $.post("http://open.douyucdn.cn/api/RoomApi/live", {
            page: 1
        }, function(data) {
            console.log(3.2, data);
        })

 $.ajax({
            type: "get",
            url: "http://open.douyucdn.cn/api/RoomApi/live",
            data: {
                page: 1
            },
            success: function(data) {
                console.log(3.3, data);
            }
        })

2.3 axios封装的ajax请求方式

使用axios前必须引入插件


<script src="https://unpkg.com/axios/dist/axios.min.js"></script>


get请求方式:

axios.get("http://open.douyucdn.cn/api/RoomApi/live", {
            params: {
                page: 1
            }
        }).then(function(res) {
            console.log(4.1, res.data);
        })


post请求方式:


axios.post("http://open.douyucdn.cn/api/RoomApi/live", "page=1", {
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            }
        }).then(function(res) {
            console.log(4.2, res.data);
        })


  • post请求数据没有在params字段中,get请求数据在params字段中
  • axios请求的回调函数在then函数中,而不是第三个参数

2.4 fetch函数请求

ES6中提供了一个函数fetch,是对原生ajax的封装,不需要插件,直接调用


fetch("http://open.douyucdn.cn/api/RoomApi/live?page=1").then(function(res) {
  // res是响应信息对象,里边没有响应体数据,需要使用json函数解析后拿到响应体
      console.log(4, res);
      var obj = res.json(); //调用json函数解析数据,返回的是promise对象
            console.log(5, obj);
      obj.then(function(data) { //使用promise对象调用then函数拿到数据
                console.log(data);
            })
    
            //实际开发直接写 
            res.json().then(function(data) {
                console.log(6, data);
            })
     })


  • 响应数据也是通过then函数拿到,而回调函数的res是响应对象,没有响应体数据,需要解析之后才能拿到响应体数据,只能解析一次。解析之后是个promise对象直接调用then拿到data数据

报错:TypeError: Response.json: Body has already been consumed.(响应体已经被解析过了)

原因:res调用json()只能调用一次,也就是只能解析一次