前言
由于浏览器的同源策略,当我们请求网络资源时,所在页面的url中的协议,端口,域名其中一个与请求资源的url不同,都会出现跨域的问题。但是浏览器不能没有这个策略,这样会很危险,像csrf,xss攻击等**。那么这里有个容易理解错误的地方,跨域并不是说服务器没法返回资源给浏览器,而是浏览器没办法正确拿到,这不是服务器的问题。**但是也不是所有的请求都是这样的,像表单提交就不存在什么跨域问题,因为表单不需要服务器返回数据给它,它只负责提交就好了。

几种解决跨域问题的方法
jsonp
jsonp主要是利用了script标签的src属性不受同源策略的影响,通过后端的配合从而解决跨域问题
下面举个栗子:
我们在页面加载完毕后就发起get请求,请求的url是本机的8080端口

 axios.get('http://127.0.0.1:8080')

打开控制台发现报了跨域的错误(这里说明一下,5500端口是vscode的一个插件搭建的服务器)

然后我们利用jsonp来让浏览器可以正常的接收到服务器返回的数据,jsonp是需要后端配合使用的,先来看代码,后面再仔细捋一遍
前端代码:
这里创建了一个script标签,然后将它的src属性赋值为请求资源的url地址,并且携带query参数过去,这里的query参数callback=handle中的handle在前端是一个函数,随后将script放入页面,一旦放入页面,scr就会去请求资源了

function handle (data) {console.log(data)}const script = document.createElement('script')
script.src = 'http://127.0.0.1:8080/?callback=handle'
document.body.insertBefore(script, document.body.firstChild)

后端代码:
后端这里解析了query参数,将callback的值单独拿出来,然后通过这个值来返回数据**(这里由于懒就直接用了querystring,建议还是使用URLSearchParams,因为vscode提醒我querystring废弃了)**

const http = require('http')
const url = require('url')
const querystring = require('querystring')
const server = http.createServer()
server.on('request', (req, res) => {const { query } = url.parse(req.url)const { callback } = querystring.parse(query)res.end(`${callback}('hello')`)
})
server.listen(8080, () => {console.log('8080端口监听中...')
})

结果:

我们再来捋一遍,首先src 不会受到同源策略的影响,所有利用script标签去请求不同源的资源不会报错,那么需要拿到服务器的数据,该怎么办?咱就这么想,服务器返回的数据是需要被解析的,那么就让服务器返回数据时调用一个函数,这个函数的形参就是服务器返回的数据(这个需要服务器配合的),所以我们指定一个query参数过去,让服务器去解析出需要调用的函数,并且给这个函数传参,比如上面后端返回的数据就是handle(‘hello’),那么浏览器收到以后一解析就去运行这个函数了
jsonp虽然可以解决跨域的问题,但是只针对get请求,没见过src是post请求的吧

CORS
我们上面报错的那个截图其实也提到了一个Access-Control-Allow-Origin 这个东西,这个东西是在后端配的,翻译一下就是允许跨域的源,这样一翻译就很明白了吧,就是在后端设置一个这个东西,表示哪些源是可以允许跨域的,如果还是不能理解也没关系,举个栗子嘛:
前端代码:请求本机8080端口资源

const promise = axios.get('http://127.0.0.1:8080')
promise.then((msg) => {console.log(msg.data)
}).catch((err) => {console.log(err)
})

后端代码:设置所有的源都可以跨域

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {res.setHeader('Access-Control-Allow-Origin', '*')res.end(`hello`)
})
server.listen(8080, () => {console.log('8080端口监听中...')
})

结果:

这里可能有些小伙伴不知道什么是源,你可以打开控制台,点击network,然后点击你请求到的资源,就可以看见如下信息:

看见请求标头里面有个Origin了吗,那个就是源,也就是说浏览器本来拿不到这个资源的,但是乍一看,这个源可以共享资源,于是就放行了。当然最好不要设置通配符,还是和前后端一起配合协商比较好,比如我们这个简单例子就可以把通配符改成5500这个源。
当然,不止这么简单设置一下就好了。
cors请求分为简单请求,和非简单请求,只要同时满足以下条件,就属于简单请求。

请求方法是以下三种方法之一:

HEAD
GET
POST
HTTP的头信息不超出以下几种字段:

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问

请求中没有使用 ReadableStream 对象

简单请求

如果是简单请求的话,Access-Control-Allow-Origin是不可省略的,否则请求按失败处理,除此之外还有Access-Control-Allow-Credentials表示请求中是否包含cookie信息,以及Access-Control-Expose-Headers,它表示XMLHttpRequest对象的getResponseHeader()方法可以拿到的额外字段(默认只能拿到六个字段)

预检请求

凡是不同时满足上面条件,就属于非简单请求。
当请求存在跨域资源共享(CORS)并且是非简单请求,就会触发CORS的预检请求,预检请求用的请求方法是OPTIONS。
如果后端采用token检验机制,前端发送请求必须将token放到请求头中,那么就需要传输自定义Header信息、则请求头中的Content-Type=“application/json”,就会形成非简单请求。
下面我们来简单演示一下复杂请求:
前端代码:把请求改为put请求

const promise = axios.put('http://127.0.0.1:5000')
promise.then((msg) => {console.log(msg.data)
}).catch((err) => {console.log(err)
})

后端代码:为了可以利用钩子,所以用express,就是说在进入app.put都要先执行app.use,随后才可以放行

const express = require("express");
const app = express();
// 实现CORS
app.use(function (req, res, next) {//允许哪个源可以共享资源res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')// 允许哪个方法访问服务器res.setHeader('Access-Control-Allow-Methods', 'PUT')// 预检的存活时间res.setHeader('Access-Control-Max-Age', 6)next();
});
app.put("/", (req, res) => {res.send('hello put method')
})app.listen(5000, () => {console.log("5000")
})

随后打开控制台,可以发现出现了两次请求,一次是put请求,一次是option请求,仔细观察也可以看见浏览器标识了option请求为预检,为什么发送了一次option请求,就是因为这是一次复杂请求,所以触发了option请求,但是我写的代码并没有对option请求做出响应处理,最好还是做一下处理。



这里的后端代码其实可以写的更严谨一点,不局限于这几个字段,还有一些允许携带cookie什么什么的请求头,也可以根据实际需求去加,所以说后端是cors通信的关键

代理服务器
原理

跨域的问题根本原因就是返回数据的服务器和请求数据的页面不是一个源,那么就申请一个代理服务器,这个代理服务器和页面在同一个源,所以不会出现跨域的问题,那么这个代理服务器上没有我们需要的数据,所以就把这个请求再转发给有这个数据的服务器上,由于服务器和服务器之间通信不会出现跨域的问题,因为同源策略是浏览器上的,和服务器没关系,所以最后就可以成功把数据请求返回给浏览器。
举个栗子:
假设有个5000端口服务器:在这个服务器上有个login接口,这个接口返回了一些json数据,现在我们有个运行在3000端口的页面需要这些json数据,直接请求会出现跨域的问题,所以我们可以先去请求3000端口,把3000端口当作一个转接器,从而得到数据
这里的5000端口的express是我自己简单封装的一个类,不是express框架,所以写法有点不一样

//5000端口服务器
const express = require('./express')const app = new express()
app.use('/login', (req, res) => {res.end(`{name:'renchel',age:19}`)//需要的数据
})
app.listen(5000, (err) => {if (err) {return}console.log("服务器已经启动,端口5000")
})

这里的3000端口是express框架,问我为啥5000不也用express框架写,那就是懒得写,因为5000端口是之前写的,我直接拿来用了

//3000端口服务器
const express = require('express')
const fs = require('fs')
const axios = require('axios')const app = express()app.get('/', (req, res) => {console.log('/')fs.readFile('./index.html', (err, data) => {if (!err) res.end(data)})
})
app.get('/login', (req, res) => {let data;(async () => {data = await axios.get('http://127.0.0.1:5000/login')res.end(data.data)//data.data是从5000端口请求回来的数据})()})app.listen(3000, (err) => {if (err) {return}console.log("服务器已经启动,端口3000")
})
//运行在3000端口的页面
<body><button>button</button><script>const btn = document.querySelector('button')btn.addEventListener('click', async () => {const data = await axios.get('http://127.0.0.1:3000/login')console.log(data)})</script>
</body>

现在捋一遍代码,有个运行在3000端口的页面有个按钮,点击按钮向3000端口的login接口请求数据,3000端口的login接口没有数据于是把这个请求转发给了5000端口的服务器,最后返回回来的数据再通过3000端口返回给页面
效果:

可以看见数据成功请求回来了。

总结
CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
JSONP只支持get请求,而且无法知晓请求的数据是否成功,如果一直卡在请求中,我们也不知道。
日常工作中,用得比较多的跨域方案是cors和Proxy代理服务器,Proxy主要就是利用同源策略对服务器不起作用。

源码附件已经打包好上传到百度云了,大家自行下载即可~

链接: https://pan.baidu.com/s/14G-bpVthImHD4eosZUNSFA?pwd=yu27
提取码: yu27
百度云链接不稳定,随时可能会失效,大家抓紧保存哈。

如果百度云链接失效了的话,请留言告诉我,我看到后会及时更新~

开源地址:http://github.crmeb.net/u/lsq
————————————————
版权声明:本文为CSDN博主「仙凌阁」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39221436/article/details/126123030

几种常见的跨域解决方法相关推荐

  1. jquery、javascript实现(get、post两种方式)跨域解决方法

     jquery.javascript实现(get.post两种方式)跨域解决方法 一.实现get方式跨域请求数据 浏览器端 <script> $(document).ready(fun ...

  2. React项目中请求跨域解决方法

    React项目中请求跨域解决方法 今天经理给我了一个React项目地址,让我拉下来并跑起来,拉下来运行起来后,发现所有的请求都失败了,并且都是由于跨域问题导致的.花了点时间,解决了这个问题,在这里记录 ...

  3. 跨域解决方法——jsonp原理

    跨域解决方法--jsonp原理 一个域名地址的组成: 当协议.子域名.主域名.端口号任意一个不相同时,都算作不同域,不同域之间相互请求资源,就算做"跨域".由于浏览器同源策略的限制 ...

  4. JavaScript跨域解决方法大全

    跨域的定义:JavaScript出于安全性考虑,同源策略机制对跨域访问做了限制.域仅仅是通过"URL的首部"字符串进行识别,"URL的首部"指window.lo ...

  5. 5种处理js跨域问题方法汇总(转载)

    1.JSONP跨域GET请求 ajax请求,dataType为jsonp.这种形式需要请求在服务端调整为返回callback([json-object])的形式.如果服务端返回的是普通json对象.那 ...

  6. Js跨域解决方法总结

    本文转载自网易博客 出于安全性的考虑,在AJAX应用中,浏览器通常都会限制跨域提交数据.但由于经常和其他部门有接口对接的业务需求,需要跨域获取数据. IE对于跨域访问的处理是,弹出警告框,提醒用户.如 ...

  7. 电脑qq收藏在哪里_电脑突然死机了怎么办?几种常见电脑故障及其解决方法

    家用.办公电脑使用难免会遇到各种的故障,而最最常见的就是电脑死机了,怎么办?今天就常见的电脑死机问题及方法进行一下汇总,喜欢的朋友不妨关注或收藏哦.希望对您有所帮助! 01 系统宕机 现象:桌面被锁定 ...

  8. frame页面地址转向跨域解决方法

    mainFrame.htm下有两个iframe,左边是left.aspx,frame名是leftFrame,右边名是是mainFrame left.aspx包含了很多的连接,linka和linkc连到 ...

  9. php 播放多个音乐,meting 音乐播放插件多域名跨域解决方法

    换了模板使用了meting音乐播放插件由于我解析了两个域名 一个是顶级域名nnnuo.com,还有个www二级域名. 但是meting插件在后台设置云解析地址的时候只要是使用其中一个域名另外一个域名访 ...

最新文章

  1. Xcode中如何解决无法使用svn命令行的问题
  2. Spring 的前世今生
  3. Vue 官方团队的 57 个技术分享,你看懂了几个?
  4. 路由器端口转发linux服务器端口映射,路由器端口映射怎么设置?
  5. 一功能简单的BBS系统源代码
  6. CDISC SDTM AE domain学习笔记 - 2
  7. JavaScript 重定向
  8. 中文汉字错别字纠错方法
  9. 电脑启动盘引导丢失怎么办
  10. 自适应二次元紫色luo莉资源网emlog模板
  11. QT报错:Gtk-Message:Failed to load module “gail“
  12. 蓝桥杯试题 算法提高 扶老奶奶过街(C语言)
  13. java // for // 俄文字母表
  14. java计算机毕业设计老鹳窝旅游网源码+系统+数据库+lw文档+mybatis+运行部署
  15. 形式逻辑三大基本要素-推理的本质
  16. 多目标跟踪MOT入门
  17. 用python统计你的文章里每个英文单词的数量
  18. uni-app入门教程(7)第三方登录和分享
  19. 前后端结合实现amazeUI分页
  20. 项目时间(项目进度计划控制)笔记

热门文章

  1. 华为离职副总裁:年薪千万的工作感悟
  2. 移动机器人gazebo仿真(3)—替换地图并建图
  3. MySQL原生SQL语句(基础)cmd 教你入门mysql
  4. 用javascript实现控制打开网页窗口的大小 和HTML如何关闭窗口的技巧大全
  5. 浪潮as5300技术方案_浪潮AS5300/5500G2-F 存储
  6. python自学入门要多久,新手学python需要多久
  7. lvextend扩展逻辑卷大小
  8. 缓存更新的Design Pattern -- 缓存专题(2)
  9. Vue相关配置版本的查看
  10. 如何有效激励经销商(上)