jsop核心:后端将数据以函数参数的形式进行返回,利用script跨域的方式来解决跨域问题;
// index.js
const Koa = require('koa');
const Router = require('koa-router');
const views = require('koa-views');
const path = require('path');
const app = new Koa();
const router = new Router();
app.use(
views(path.resolve(__dirname, './views'), {
extension: 'ejs'
})
);
router.get('/', async ctx => {
ctx.render('./views/index.ejs', { title: 'Index' });
});
router.get('/getData', async ctx => {
const data = {
success: true,
data: {
text: 'this is jsonp api',
add: [1, 2, 3]
}
};
// 设置jsonpStr, 设置content-type和content-body
const jsonpStr = `callback(${JSON.stringify(data)})`;
ctx.type = 'text/javascript';
ctx.body = jsonpStr;
});
app.use(router.routes(), router.allowedMethods());
app.listen(3000, () => console.log('server is on port 3000'));
前端使用jsonp方法:
// index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title><%= title %></title>
</head>
<body>
<button href="/getData" class="myBtn">跳转</button>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"
></script>
<script>
const btn = document.querySelector('.myBtn');
// 前端调用jsonp的使用方法
const callback = data => console.log(data);
$('.myBtn').click(e => {
const script = document.createElement('script');
script.src = 'http://localhost:3000/getData';
document.body.appendChild(script);
document.body.removeChild(script);
});
</script>
</body>
</html>
CORS解决非同源请求的主要手段,通过添加allow-access-origin的请求头来解决上述问题
- 简单请求,服务器直接添加三个头信息
- 非简单请求,浏览器请求为同源请求,再次请求时,服务器带上头信息(多一次请求)
# 安装koa2-cors
yarn add koa2-cors
// cors.js
const Koa = require('koa');
const cors = require('koa2-cors');
const Router = require('koa-router');
const router = new Router();
const app = new Koa();
app.use(
cors({
origin: function(ctx) {
// 只有域名在localhost:3000下的请求才能被获得
return 'http://localhost:3000';
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
maxAge: 1000,
credentials: true,
allowMethods: ['GET', 'POST', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization', 'Accept']
})
);
router.get('/getUserInfo', async ctx => {
ctx.body = [{ name: 'Mike', age: 18 }];
});
app.use(router.routes(), router.allowedMethods());
app.listen(8000, () => {
console.log('server is on 8000');
});
// App.js
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import './App.css';
Axios.defaults.withCredentials = true;
function App() {
const [state, setState] = useState([]);
// 该react项目运行在3000端口
// 访问接口在8000属于跨域问题
const getUserInfo = () => Axios.get('http://localhost:8000/getUserInfo');
useEffect(() => {
async function getData() {
const { data } = await getUserInfo();
setState(data);
}
getData();
}, []);
return (
<div className="App">
<header className="App-header">
{state.map(elem => {
const { name, age } = elem;
return (
<div>
姓名:{name}, 年龄:{age}
</div>
);
})}
</header>
</div>
);
}
export default App;
返回一个字符串,配置Access-Control-Allow-Origin的头信息,用于告诉浏览器允许访问该CORS的同源请求。如果配置为一个函数,函数的第一个参数为ctx
用于配置Access-Control-Expose-Headers CORS 头信息. 可填项,为CORS请求时添加其他头字段。
配置 Access-Control-Max-Age CORS 头信息.
配置 Access-Control-Allow-Credentials CORS 头信息. 是否需要发送cookie.
配置 Access-Control-Allow-Methods CORS 头. 默认值: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
.
这里配置了maxAge和allowMethod但是好像在头信息中没有包含Acess-Control-Allow-MaxAge等字段,后面查一下原因