JavaScript学习笔记(七) 跨域问题
1、跨域问题
(1)什么是跨域问题?
什么是域?一个域由协议、域名、端口三者共同组成
什么是跨域?只要协议、域名、端口三者任意一个不同,就当作是跨域
什么是跨域问题?简单来说,就是 浏览器 不允许跨域请求资源
(2)为什么会有跨域问题?
为什么会有跨域问题?这是因为浏览器同源策略的限制
什么是同源策略?同源策略限制一个源加载的文档或脚本如何与来自另一个源的资源进行交互
为什么会有同源策略?它是一种重要的安全机制,用于隔离潜在的恶意文件
(3)为什么还要跨域请求?
既然说跨域请求会带来安全问题,那么为什么我们还要进行跨域请求呢?
这个也是莫得办法呀,有的时候同一家公司会有多个不同的子域,需要相互请求资源
2、跨域问题的解决方案
(0)准备环境
我们先来搭建一个简单的测试环境,整体的文件结构如下:
+ back_end
+ node_modules
- package-lock.json
- package.json
- server.js
+ front_end
+ node_modules
- index.vue
- package-lock.json
- package.json
① 后端部分【Node.js + Express】
在 back_end
目录下运行 npm init
命令,创建 package.json
文件
接着使用 npm install --save express
命令安装 express
然后创建一个名为 server.js
的文件,在文件中输入以下内容:
// 引入 express 模块
const express = require('express')
// 创建 express 实例
var app = express()
// 设置路由
app.get('/api', function(req, res) {
// console.log(req.query.message)
res.json({
'message': req.query.message
})
})
app.get('/', function(req, res) {
res.send('Hello World')
})
// 启动服务器,监听端口 5000
var server = app.listen(5000)
命令行运行 node server.js
启动 express 服务器(127.0.0.1:5000
)
② 前端部分【Vue + jQuery】
同样在 front_end
目录下运行 npm init
命令,创建 package.json
文件
接着使用 npm install --save jquery
命令安装 jquery
使用 npm install -g @vue/cli
命令全局安装 Vue
使用 npm install -g @vue/cli-service-global
命令全局安装一个拓展
然后创建一个名为 index.vue
的文件,在文件中输入以下内容:
<template>
<div>
<input class="enter-message" v-model="message" />
<button class="send-message" v-on:click="submit">Submit</button>
</div>
</template>
<script>
import $ from 'jquery'
import qs from 'qs'
export default {
data: function () {
return {
message: 'Hello'
}
},
methods: {
submit: function() {
let params = qs.stringify({
'message': this.message
})
$.ajax({
type: 'GET',
url: 'http://127.0.0.1:5000/api' + '?' + params,
success: function(value) {
console.log('success')
console.log(value)
},
error: function(error) {
console.log('error')
console.log(error)
},
})
}
}
}
</script>
<style>
.enter-message {
display: block;
margin: 20px;
padding: 2px;
}
.send-message {
display: block;
margin: 20px;
}
</style>
命令行运行 vue serve index.vue
快速启动一个服务器(127.0.0.1:8080
),搭载应用
③ 测试
由于两者端口不同,所以毫无疑问的出现了跨域问题
下面我们讨论两种常见的跨域问题解决方案,先摆上最终效果
最终结果:
(1)JSONP
简单来说,JSONP 通过 <script> 标签的 src 属性不受同源策略的限制获取跨域资源
具体来说,前端在参数中发送一个回调函数的函数名,后端返回这个函数的调用,并把数据放在这个函数的参数上
注意,JSONP 只支持 GET 方法,前后端的核心代码如下
后端代码:
app.get('/api', function(req, res) {
let func = req.query.callback
let data = { 'message': req.query.message }
res.send(func + '(' + JSON.stringify(data) + ')')
})
前端代码:
methods: {
submit: function() {
let url = 'http://127.0.0.1:5000/api'
let data = { 'message': this.message }
this.jsonp(url, data).then(function(res) {
console.log(res)
})
},
jsonp: function(url /*String*/, data /*Object*/) {
return new Promise(function(resolve, reject) {
window.handleResponse = function(result) {
resolve(result)
}
let script = document.createElement('script')
script.type = 'text/javascript'
script.src = url + '?' + qs.stringify(data) + '&callback=handleResponse'
document.getElementsByTagName('head')[0].appendChild(script)
setTimeout(function() {
document.getElementsByTagName('head')[0].removeChild(script)
}, 1000)
})
}
}
实际上,Node.js 和 jQuery 都提供了更加便捷的方式使用 JSONP,前后端的核心代码如下
后端代码:
app.get('/api', function(req, res) {
res.jsonp({ // 改成 jsonp
'message': req.query.message
})
})
前端代码:
methods: {
submit: function() {
let params = qs.stringify({
'message': this.message
})
$.ajax({
type: 'GET',
url: 'http://127.0.0.1:5000/api' + '?' + params,
dataType: "jsonp", // 指定 jsonp
success: function(value) {
console.log(value)
},
error: function(error) {
console.log(error)
},
})
}
}
(2)CORS
跨域资源共享(Cross-Origin Resource Sharing,CORS)是 W3C 中定义的标准
它定义了在跨域请求资源时,浏览器与服务器如何进行交互,它们通过 HTTP 头部传达这种信息
我们只需要修改后端返回的 Response 的 HTTP 头部即可,前端代码无需修改
后端代码:
app.get('/api', function(req, res) {
res.header('Access-Control-Allow-Origin', '*') // 加上 HTTP 头部即可
res.json({
'message': req.query.message
})
})
【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记 】