跨域那些事儿:CORS和JSONP,你的浏览器"管家"如何帮你"偷渡"数据?
嘿,各位掘友们!今天咱们来聊聊前端开发中一个让人又爱又恨的话题------跨域。你是不是也遇到过这样的情况:明明代码写得好好的,一运行,浏览器就给你甩脸子,报错说"跨域了!"?别急,这可不是你的错,而是浏览器这位"管家"太尽职尽责了!
⚠️ 什么是跨域?浏览器"管家"的同源策略

想象一下,你住在一个戒备森严的小区里,小区规定:只有来自同一个楼栋、同一个单元、门牌号也一样的快递,才能直接送到你家门口。如果快递是从隔壁小区、甚至隔壁城市寄来的,那对不起,得先经过物业的严格审查,才能决定能不能放行。
在浏览器世界里,这个"小区"就是"源"(Origin),而"物业"就是"同源策略"(Same-Origin Policy)。当你的前端代码(比如运行在 http://a.com
)想要请求后端数据(比如在 http://b.com
),浏览器一看,哎呀,这俩"源"不一样啊!一个 a.com
,一个 b.com
,这不就是"隔壁小区"来的快递嘛!为了你的安全,浏览器"管家"就会立刻启动同源策略,把这个请求给拦下来,这就是所谓的"跨域问题"。
同源策略是浏览器的一项重要安全功能,它限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。简单来说,就是协议、域名、端口号三者都相同,才算是"同源"。只要有一个不同,就是"跨域"。
那么,问题来了,我们总不能因为浏览器"管家"太负责,就放弃和"隔壁小区"的兄弟们交流吧?当然不能!所以,就有了各种"偷渡"数据的方法,今天咱们就来聊聊其中两种最常用的------CORS和JSONP。
✨ CORS:后端"开绿灯",让数据"光明正大"地通行

CORS(Cross-Origin Resource Sharing),中文名叫"跨域资源共享",听起来是不是很高大上?其实它的原理很简单,就像是后端服务器给浏览器"管家"打了个招呼:"嘿,老伙计,a.com
那边来的请求是自己人,放行吧!"
🔧 CORS的原理
当浏览器发起一个跨域请求时,它会先发送一个"预检请求"(Preflight Request),问问服务器:"我能向你请求数据吗?我用的是POST方法,还带了自定义的请求头。"服务器收到预检请求后,如果允许这个跨域请求,就会在响应头中添加一些特殊的字段,告诉浏览器:"可以!没问题!"浏览器收到这个"通行证"后,才会真正发送实际的请求。
而如果服务器不允许,那浏览器就会直接报错,连实际请求都不会发出去。所以,CORS的精髓在于,它需要后端服务器的配合。后端服务器通过设置响应头,来告诉浏览器哪些源可以访问自己的资源。
🚀 如何使用CORS(以Express为例)
在Node.js的Express框架中,使用CORS非常简单,只需要一个第三方中间件 cors
就能搞定。就像你请了个专业的"外交官",帮你处理和浏览器"管家"的沟通。
-
安装
cors
中间件bashcnpm i cors
或者使用npm:
bashnpm install cors
-
配置
cors
中间件在你的Express应用中,引入并使用
cors
中间件:javascriptconst cors = require('cors'); app.use(cors());
app.use(cors())
这一行代码,就像是给你的Express应用开了一个"全球通行证",允许所有来源的跨域请求。当然,你也可以配置更精细的权限,比如只允许特定的域名访问,或者只允许特定的HTTP方法。
💡 CORS的核心:Access-Control-Allow-Origin
CORS的核心就在于服务器响应头中的 Access-Control-Allow-Origin
字段。当服务器设置 res.header('Access-Control-Allow-Origin', '*')
时,就表示允许所有来源 的请求访问。这个 *
就像是一个万能钥匙,告诉浏览器"管家":"所有人都欢迎!"
当然,在实际项目中,为了安全起见,我们通常会指定具体的域名,而不是使用 *
。比如,如果你只允许 http://a.com
访问,那就设置成 res.header('Access-Control-Allow-Origin', 'http://a.com')
。
CORS是目前主流的跨域解决方案,因为它符合W3C标准,并且由浏览器和服务器共同协作完成,安全性也比较高。
🔄 JSONP:曲线救国,利用<script>
标签的"特权"

如果说CORS是后端"开绿灯",那么JSONP(JSON with Padding)就是一种"曲线救国"的策略,它利用了HTML中<script>
标签的一个"特权":<script>
标签的 src
属性可以加载任意域的JavaScript文件,而不受同源策略的限制。这就像是小区里的一个特殊通道,专门用来运送"特殊货物",而这个"特殊货物"就是我们的数据。。
💡 JSONP的定义与原理
JSONP的原理很简单:前端通过动态创建<script>
标签,向服务器发送请求。服务器收到请求后,不是直接返回JSON数据,而是返回一段JavaScript代码,这段代码会调用前端预先定义好的一个回调函数,并将数据作为参数传递进去。这样,前端就能在回调函数中拿到数据了。
举个例子,你想要从 http://b.com
获取一份菜单。你不能直接去拿,但是你可以给 b.com
的餐厅老板打个电话,告诉他:"老板,我叫小明,你把菜单给我的时候,能不能用'小明点餐'这个方式告诉我?"老板说:"没问题!"于是,他把菜单用"小明点餐(菜单内容)"这种格式告诉你。你一听到"小明点餐",就知道这是给你的,然后就能处理菜单内容了。
🚀 如何使用JSONP
前端
前端需要定义一个回调函数,并动态创建<script>
标签,将回调函数名作为参数传递给后端。
html
<script>
function getData(data) {
console.log(data);
}
</script>
<script src="http://127.0.0.1:3000/api/v1/course/find?callback=getData"></script>
在这段代码中,getData
就是我们定义的回调函数。<script>
标签的 src
属性指向后端接口,并且通过 callback=getData
将回调函数名传递过去。当后端返回数据时,就会调用 getData
函数。
后端
后端收到请求后,会获取前端传递过来的回调函数名,然后将数据包裹在这个回调函数中,再作为JavaScript代码返回给前端。
javascript
router.get('/find', (req, res) => {
const getData = req.query.callback;
const data = { name: '小滴课堂' };
res.send(`${getData}(${JSON.stringify(data)})`);
});
后端通过 req.query.callback
获取到前端传递过来的回调函数名 getData
。然后,将 data
对象通过 JSON.stringify
转换成字符串,并将其包裹在 getData()
函数中,最终返回 getData({"name":"小滴课堂"})
这样的字符串。前端接收到这段字符串后,会将其作为JavaScript代码执行,从而调用 getData
函数并获取到数据。
⚠️ 注意事项
JSONP虽然能解决跨域问题,但它有一个明显的缺点:JSONP只支持GET请求方式 。因为它是通过<script>
标签的 src
属性来发送请求的,而 src
属性只能发起GET请求。所以,如果你需要发送POST、PUT、DELETE等请求,JSONP就无能为力了。
另外,JSONP的安全性也相对较低,因为它会执行从其他域加载的JavaScript代码,如果服务器返回的代码被恶意篡改,可能会导致安全问题。因此,在实际开发中,CORS是更推荐的跨域解决方案。
总结
今天我们深入探讨了跨域问题以及两种常见的解决方案:CORS和JSONP。CORS是目前主流且推荐的解决方案,它通过后端设置响应头来告知浏览器允许跨域请求,符合W3C标准,安全性高。而JSONP则是一种利用<script>
标签特性的"曲线救国"方案,虽然简单易用,但仅支持GET请求且存在一定的安全风险。
在实际开发中,我们应优先考虑使用CORS来解决跨域问题。只有在特定场景下,例如需要兼容老旧浏览器或者后端无法配合设置CORS时,才考虑使用JSONP。理解这两种机制,能让你在面对跨域问题时游刃有余,不再被浏览器"管家"的"同源策略"所困扰!
希望这篇博客能帮助你更好地理解跨域问题,如果你有任何疑问或想法,欢迎在评论区交流!