关于前端跨域问题用的最多的应该是cors,关于jsonp主要用于get请求,前端代理也仅适用于开发阶段,上线阶段还是要后台配置跨域问题。
今天主要介绍下面三种,会配合nodejs一起呈现
1.jsonp (主要用于get请求,原理是利用script标签可以请求不同域)
jsonp需要前后端配合,约定回调函数的名字,我是在3000的端口下请求3001的服务器,下面直接看代码
前端(index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="name">
</ul>
<script>
request()
function request() {
var
js = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
js.src = 'http://localhost:3001/api/users?callback=userAdd';
head.appendChild(js);
}
function userAdd(data) {
document.getElementById("name").innerHTML = data.map((item) => {
console.log(item)
return `<li>${item.name}</li>`
}).join('')
}
</script>
</body>
</html>
nodejs(3001服务器代码)
const http = require("http")
const fs = require('fs')
const qs = require('querystring')
const URL = require('url')
const users = [
{
name: 'jackie',
age: 27
},
{
name: 'Tom',
age: 20
}
]
const server = http.createServer((req, res) => {
const {url, method} = req;
const{callback} = URL.parse(req.url,true).query//需要使用nodejs的url模块序列化参数
console.log(callback)
if (callback) {//判断传递参数的回调
res.writeHead(200, {'Content-Type': 'text/javascript'})
res.end(callback+`(${JSON.stringify(users)})`);//把获取的函数名和要传递的数据拼接返回给前端
//res.end(callback+"("+JSON.stringify(users)+")");
}
else {//如果没有传递参数的回调处理
res.writeHead(200, {'Content-Type': 'application/json'})
res.end(JSON.stringify(users))
}
})
server.listen(3001)
(3000服务器代码)
const http = require("http")
const fs = require('fs')
const qs = require('querystring')
const server = http.createServer((req, res) => {
const {url, method} = req;
if (url === '/' && method === 'GET') {
console.log('11')
fs.readFile('./index.html', (err, data) => {
if(err) {
res.writeHead(500, {'Content-Type': 'text/plain'})
res.end('server error')
return
}
res.writeHead(200, {'Content-Type': 'text/html'})
res.end(data)
})
})
server.listen(3000)
2.cors
在说到cors解决跨域之前我们有必要来了解一下简单请求和非简单请求
满足以下两大条件,就属于简单请求,那么不满足的就是非简单请求了
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
post请求的Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
先来说简单请求的跨域解决,通过3001的服务器访问3000的接口,先上代码
前端(index.html)(这是一个简单请求,post+Content-Type(application/x-www-form-urlencoded))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:3000/api/users" method="post">
<input type="text" name="name">
<input type="number" name="age">
<input type="submit" value="新增用户">
</form>
<ul id="name">
</ul>
</body>
</html>
nodejs(3000端口)
const http = require("http")
const fs = require('fs')
const qs = require('querystring')
const users = [
{
name: 'jackie',
age: 27
},
{
name: 'Tom',
age: 20
}
]
const server = http.createServer((req, res) => {
const {url, method} = req;
console.log(url,method)
if (url === '/' && method === 'GET') {
console.log('11')
fs.readFile('./index.html', (err, data) => {
if(err) {
res.writeHead(500, {'Content-Type': 'text/plain'})
res.end('server error')
return
}
res.writeHead(200, {'Content-Type': 'text/html'})
res.end(data)
})
} else if (url === '/api/users' && method === 'GET') {
console.log('22')
//用writeHead再加setHeader不行,为什么请去查阅文档
//res.writeHead(200, {'Content-Type': 'application/json'})
res.setHeader('Content-Type', 'application/json')
//简单请求加cors
res.setHeader('Access-Control-Allow-Origin','http://localhost:3001');
res.setHeader("Access-Control-Allow-Credentials",true);
//res.setHeader('Access-Control-Allow-Origin','http://localhost:3001')
res.end(JSON.stringify(users))
} else if (url === '/api/users' && method === 'POST') {
//接受请求参数
let body = []
req.on('data', (chunk) => {
console.log(chunk)
body.push(chunk);
}).
on('end', () => {
//数据接收完毕之后把body转换为完整的buffer
body = Buffer.concat(body).toString();
const user = qs.parse(body)
users.push(user);
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end('add success!')
})
}else if(method=='OPTIONS' && url=='/api/users'){
console.log('444')
res.writeHead(200,{
"Access-Control-Allow-Origin":"http://localhost:3001",
"Access-Control-Allow-Headers":"X-Token,content-type",
"Access-Control-Allow-Methods":"GET,POST,PUT",
"Access-Control-Allow-Credentials":"true",
})
res.end();
}
})
server.listen(3000)
(3001端口)
const http = require("http")
const fs = require('fs')
const qs = require('querystring')
const URL = require('url')
const server = http.createServer((req, res) => {
const {url, method} = req;
if (url === '/' && method === 'GET') {
fs.readFile('./index.html', (err, data) => {
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'})
res.end('server error')
return
}
res.writeHead(200, {'Content-Type': 'text/html'})
res.end(data)
})
}
})
server.listen(3001)
效果图(新增用户成功)
非简单请求会先发一个options请求
在node后台加个判断:
else if(method=='OPTIONS' && url=='/api/users'){
console.log('444')
res.writeHead(200,{
"Access-Control-Allow-Origin":"http://localhost:3001",
"Access-Control-Allow-Headers":"X-Token,content-type",
"Access-Control-Allow-Methods":"GET,POST,PUT",
"Access-Control-Allow-Credentials":"true",
})
res.end();
}
前端在3001下请求3000的接口
axios.post("http://localhost:3000/api/users",{
header:{
'Content-Type':'application/json'
}
})
.then(res=>res.data).then(users=>{
console.log(users);
document.getElementById("name").innerHTML=users.map((item)=>{
console.log(item)
return `<li>${item.name}</li>`
}).join('')
})
效果图
3.代理
我用的vue-cli3.0,配置vue.config.js的 devServer
devServer: {
proxy: {
'/api': {//代理api
target: "http://ip:port",//服务器api地址
changeOrigin: true,//是否跨域
ws: true, // proxy websockets
pathRewrite: {//重写路径
"^/api": ''
}
}
}
}