前端跨域解決方案
跨域問題的產生及其價值意義
跨域(非同源策略請求),是由瀏覽器的安全機制引起的。
三者都一樣就是同源,只要有一個不同就是跨域
- 協議
- 域名
- 端口號
WEB服務器地址:http://127.0.0.1:3000/index.html
數據接口地址:http://127.0.0.1:4000/list
前端(跨域)的發展:
1、沒有專門的前端,由後端一起做。
2、前後端分開,部署的時候部署在同一個服務器,這樣就不存在跨域問題。但是,開發的時候前後端是分開的,還是會存在跨域,怎麼辦呢?有的公司要求前端也用idea進行開發,也就是前端代碼外面包一層java或其它後端代碼,運行的時候也要運行後端服務,這樣的做法給前端開發加大了難度。
- 同源策略請求 ajax / fetch
- 跨域傳輸
部署到web服務器上:同源策略
- xampp 修改本地的host文件
127.0.0.1:1234 http://api.qq.com/
http://127.0.0.1:1234/index.html
http://api.qq.com/getData
3、前後端完全分離,分開開發,分開部署web服務器,data服務器,圖片服務器。 第三方開源的數據接口也會引起跨域(這樣的情況非常多)。
JSONP跨域解決方案的底層原理
- script
- img
- link
- iframe
- ...
=>不存在跨域請求的限制
<script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
// 這個就是因爲script標籤沒有跨域限制,所以才能成功請求加載。
核心原理圖:
react中子組件想要修改父組件中的狀態,也是傳遞一個回調函數給父組件,這個思想和JSONP的思想是一致的。
- JSONP需要服務器端的支持
- 問題:JSONP只能處理GET請求(放在?後面不安全,服務器返回的數據在瀏覽器會直接執行,如果是木馬修改的呢?也會直接執行,不安全)
舉例:
- html頁面:1.jsonp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
</head>
<body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="./1.jsonp.js"></script>
</body>
</html>
- 1.jsonp.js
$.ajax({
url: 'http://127.0.0.1:8001/list',
method: 'get',
dataType: 'jsonp', //=>執行的是JSONP的請求,這是jquery封裝的ajax的功能
success: res => {
console.log(res);
}
});
- 服務端配合:serverJSONP.js:
let express = require('express'),
app = express();
app.listen(8001, _ => { // 監聽8001端口
console.log('OK!');
});
app.get('/list', (req, res) => {
let {
callback = Function.prototype // callback如果沒有,默認爲空的函數
} = req.query;
let data = {
code: 0,
message: '返回jsonp請求的結果'
};
res.send(`${callback}(${JSON.stringify(data)})`); //=>後端需要處理好這樣的數據格式
});
CORS跨域資源共享
客戶端正常發送請求,服務端設置相關的頭信息。
- 客戶端(發送ajax/fetch請求)
axios.defaults.baseURL = 'http://127.0.0.1:8888';
axios.defaults.withCredentials = true;
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = function (data) {
if (!data) return data;
let result = ``;
for (let attr in data) {
if (!data.hasOwnProperty(attr)) break;
result += `&${attr}=${data[attr]}`;
}
return result.substring(1);
};
axios.interceptors.response.use(function onFulfilled(response) {
return response.data;
}, function onRejected(reason) {
return Promise.reject(reason);
});
axios.defaults.validateStatus = function (status) {
return /^(2|3)\d{2}$/.test(status);
}
- 服務器端設置相關的頭信息(需要處理options試探性請求)
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "http://localhost:8000"); // http://localhost:8000是允許跨域請求的地址,如果允許很多地址跨域請求,設置爲"*"
//=>*(就不能在允許攜帶cookie了) 具體地址
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,HEAD,OPTIONS");
if (req.method === 'OPTIONS') {
res.send('OK!');
return;
}
next();
});
基於http proxy實現跨域請求
http proxy =>webpack webpack-dev-server
修改webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.min.js',
path: path.resolve(__dirname, 'build')
},
devServer: {
port: 3000,
progress: true,
contentBase: './build',
proxy: { // => 以'/'開始的請求,就把請求路徑轉到 target
'/': {
target: 'http://127.0.0.1:3001',
changeOrigin: true // => 允許跨域
}
}
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
})
]
};