通常情况下,如果服务器不设置跨域头,页面是获取不到非同域下的post请求数据。利用form+iframe,通过父子页面的数据传递可以实现post跨越请求。
有两种方式可以实现父子页面的数据传递。针对支持HTML5的浏览器可以使用postMessage来实现,这种方式支持不同主域页面之间的数据传递。而另外一种是使用回调函数来实现,这种方式只支持同主域(子域名可能不同)页面之间的数据传递。
回调函数方式
示例:
<!DOCTYPE html>
<html>
<head>
<title>post跨越请求</title>
<meta charset="utf-8">
</head>
<body>
<button onclick="doPost('testFn');">发送请求</button>
<script type="text/javascript">
document.domain="test.com";
function doPost(callbackName) {
var obj = {
callbackName: callbackName
}
//解决ie7以下设置name无效的bug
var iframe = document.all ? document.createElement('<iframe name="myFrame"></iframe>') : null;
if(!iframe) {
iframe = document.createElement('iframe');
iframe.name = 'myFrame';
}
var form = document.createElement('form');
iframe.style.display = 'none';
form.style.display = 'none';
form.target = 'myFrame';
form.action = 'http://a.test.com:8080/api/1';
form.method = 'post';
for(var key in obj) {
var input = document.createElement('input');
input.name = key;
input.value = obj[key];
form.appendChild(input);
}
document.body.appendChild(iframe);
document.body.appendChild(form);
form.submit();
window[callbackName] = function(data) {
alert(data);
iframe.parentNode.removeChild(iframe);
form.parentNode.removeChild(form);
}
}
</script>
</body>
</html>
server.js
const express = require('express');
const fs = require('fs');
const mime = require('mime');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
app.use(bodyParser.json());//数据JSON类型
app.use(bodyParser.urlencoded({ extended: false }));//解析post请求数据
app.post('/api/1',function(req,res){
res.send(`<script>document.domain="test.com";window.parent.${req.body.callbackName}('服务器数据')</script>`);
});
app.post('/api/2',function(req,res){
res.send(`<script>window.parent.postMessage('服务器数据','*')</script>`);
});
app.get('/*', function(req,res){
sendFile(req, res);
});
app.listen(8080);
console.log('listen 8080')
function sendFile(req,res){
var realPath = __dirname+req.url;
var exist = fs.existsSync(realPath);
if(exist){
var file = fs.readFileSync(realPath);
res.writeHead(200, {
'Content-Type': mime.getType(path.basename(realPath)),
});
res.end(file);
console.log('send static file:'+realPath);
} else {
res.writeHead(404);
}
}
注意:
- 服务器X-Frame-Options不能设置成DENY
- 父页面和子页面的主域必须相同(上面父子页面的主域为
test.com
),并且父子页面都必须显示的设置domain为主域,即使父页面本身就是主域名。
postMessage方式
实例:
<!DOCTYPE html>
<html>
<head>
<title>post跨越请求</title>
<meta charset="utf-8">
</head>
<body>
<button onclick="doPost();">发送请求</button>
<script type="text/javascript">
function doPost() {
var iframe = document.createElement('iframe');
var form = document.createElement('form');
iframe.name = 'myFrame';
iframe.style.display = 'none';
form.style.display = 'none';
form.target = 'myFrame';
form.action = 'http://a.test.com:8080/api/2';
form.method = 'post';
document.body.appendChild(iframe);
document.body.appendChild(form);
form.submit();
window.onmessage = function(event) {
alert(event.data);
iframe.parentNode.removeChild(iframe);
form.parentNode.removeChild(form);
}
}
</script>
</body>
</html>