同源:ajax请求的url和网页所在的url具有相同的协议,域名,ip和端口
同源策略:为了数据安全,浏览器禁止通过ajax请求读取非同源的数据(注意:同源策略的两个关键点:1,浏览器 2,ajax请求)
同源策略下,浏览器会拦截跨域数据并报错,拦截的响应数据
跨域/跨源:ajax请求的url和网页所在的url具有不同的协议,域名,ip和端口
1.1 COR头跨域
原理:在ajax请求的响应头中,添加cors字段,相当于令牌/通行证,可以避免浏览器拦截这个跨域数据(浏览器在接受ajax跨域数据时,会优先判断响应头中是否存在cors字段,如果有,则不拦截数据,如果没有,则报错)
步骤:在后端服务器接口响应之前,添加响应头
// 在响应头中添加cors字段, 允许这个接口跨域访问 res.setHeader("Access-Control-Allow-Origin", "*") //或 // 使用第三方cors模块允许跨域 (可一次设置所有接口跨域) var cors = require("cors") app.use(cors())
前端代码:
这是主页
优点:简单方便,容易操作,前端请求无需修改ajax代码
缺点:数据安全性降低,完全受后端限制,前端无法控制
1.2 jsonp跨域
原理:同源策略只对ajax请求有限制,非ajax请求无限制,非ajax请求(如:form表单请求、标签属性请求,a标签的href,img标签的src,script标签的src)不会被同源策略拦截,所以使用script标签的src属性发起的跨域请求不会被同源策略拦截。
步骤:前端(3种):
1,原生的jsonp请求:
前端:
callback这个字段不能自定义,是jsonp内部规定好的字段,不用callback调用不了
后端:
app.get('/userInfo', function(req, res) { console.log(req.query) req.query.callback({ name: "李四" }) })
2,常规的开发习惯写法
前端:
后端同1一样
3,jquery封装jsonp请求,使用$.getJSON()实现
前端:
后端:
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跨域请求,获取跨域数据后返回给同源客户端
步骤:创建同源服务器,在服务器种添加代理
前端:
这是主页
后端:
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.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前必须引入插件
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()只能调用一次,也就是只能解析一次