8

前言

由于自己平时只做做demo,并没有遇到太多跨域问题,今天通过几个样例模拟实现了几种跨域方式。原文地址 传送门

本文所有样例静态服务器基于nodejs实现,代码亲测可用。测试步骤如下:

1.为了实现跨域访问的效果,需要下载http-server 作为一个服务器 npm install http-server。用来挂载静态页面 index.html 。(访问http://127.0.0.1:8080 显示index.html页面)
2.运行node创建的静态服务器node server (node搭建静态服务向下翻)

JSONP跨域

JSONP实现跨域原理

说道跨域可能最先想到就时 jsonp ,实现原理为:同源策略只限制浏览器的行为而不限制js,所以可以将请求写到 script 标签中。关键代码如下:

<script>
    function jsonpCallback(args){...}
</script>
<script src='http://127.0.0.1:3000?callback=jsonpCallback'></script>

前端发出跨域请求数据后,服务端去解析该请求:

const data={...}
const callback = req.parse(req.url,true).query.callback
res.writeHead(200)

//最关键的一步,拼接回调函数和作为函数参数的数据data
res.end(`${callback}(${JSON.stringfy(data)})`)

浏览器接收到服务端返回的响应,由于发起请求的是script,相当于直接调用方法并传入参数

具体实现案例

(服务端代码,node.js)

const url = require('url');

require('http').createServer((req, res) => {
    const data = {
        x: 10
    };
    const callback = url.parse(req.url, true).query.callback
    res.writeHead(200);
    //回调原页面上函数处理返回结果
    res.end(`${callback}(${JSON.stringify(data)})`);

}).listen(3000, '127.0.0.1');
console.log('服务器已启动...');

(前端)

<!DOCTYPE html>
<html lang="en">
    <head>
        <title></title>
        <meta charset="UTF-8">
    </head>
    <body>
        <script>
            function jsonpCallback(data){
                alert('跨域成功')
            }   
        </script>
        <script src="http://127.0.0.1:3000/?callback=jsonpCallback"></script>
    </body>
</html> 

JSONP解决Ajax跨域问题

如果是采用js原生,可以参考传送门

CORS跨域

实现CORS跨域的思想为:CORS通信和Ajax同源请求没有区别,关键在于服务端的配置。要想实现CORS通信,服务端必须要设置一个自定义头Access-Control-Origin:''来允许跨域访问

(样例仅做了一个最简单的GET请求demo服务端代码)

require('http').createServer((req,res)=>{
    res.writeHead(200,{
        'Access-Control-Allow-Origin':'http://127.0.0.1:8080'
    })
    res.end('这是来自端口3000的信息,收好了~')
}).listen(3000,'127.0.0.1')

(前端代码)

<!DOCTYPE html>
<html lang="en">
    <head>
        <title></title>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    <script>
        //CORS通信和同源ajax通信没有区别,重点是子服务器端要设置可以允许跨域的自定义头
        const ajax = new XMLHttpRequest()
        ajax.open('post','http://127.0.0.1:3000',true) 
        ajax.onreadystatechange = function(){
            if(ajax.readyState===4){
                if(ajax.status>=200&&ajax.status<300||ajax.status===304){
                    alert(ajax.responseText)
                }
            }
        }
        ajax.send(null)
    </script>
</html>

cors实现跨域的优点是不但能实现 GET , POST 等简单请求,还能实现 PUT 等非简单请求跨域

ServerProxy服务器代理

ServerProxy服务器代理实现跨域的核心思想是:将跨域请求操作发送给服务端,由服务端代为请求,然后将请求结返回过来
这里以获取 CNode:Node.js专业中文社区 论坛上一些数据为场景。如通过 https://cnodejs.org/api/v1/to...,当时因为不同域,所以你可以将请求后端,让其对该请求代为转发。

const url = require('url')
const http = require('http')
const https = require('https')

const server = http.createServer((req,res)=>{
    const path = url.parse(req.url).path.slice(1)
    if(path==='topics'){
        https.get('https://cnodejs.org/api/v1/topics', (resp) => {
            let data=''
            resp.on('data',chunk=>{
                data+=chunk
            })
            resp.on('end',()=>{
                res.writeHead(200,{
                    'Content-Type':'application/json;charset=utf-8'
                })
                res.end(data)
            })
        })
    }
}).listen(3000,'127.0.0.1')

console.log('服务器已启动...');

postMessage

postMessage是HTML5新增的一项功能,postMessage() 方法允许来自不同源的脚本采用异步方式进行有限通信可以实现跨文本文档,多窗口,跨域消息传递。
利用postMessage不能和服务端交换数据,只能在两个窗口 <iframe> 之间交换数据

postMessage(data,origin)方法接收两个参数
data:html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象.然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化
oringin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略。这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"

创建一个postMsg.html,创建一个iframe。使用postMessage可以向 http://127.0.0.1:8081 发送消息,然后监听 message ,可以获得其他文档发来的消息

<!DOCTYPE html>
<html lang="en">
    <head>
        <title></title>
        <meta charset="UTF-8">
    </head>
    <body>
        <iframe src="http://127.0.0.1:8081/recieve.html" style="display:none;"></iframe>
        <script>
            window.onload = function(){
                let targetOrigin='http://127.0.0.1:8081'
                window.frames[0].postMessage('我要给你发消息了..!',targetOrigin)
            }
            window.addEventListener('message',function(e){
                console.log('postMsg.html 接收到的消息',e.data)
            })
        </script>
    </body>
</html>

(创建recieve.html文件)

<!DOCTYPE html>
<html lang="en">
    <head>
        <title></title>
        <meta charset="UTF-8">
    </head>
    <body>
        <script>
            window.addEventListener('message',function(e){
                if(e.source!=window.parent){
                    return
                }
                let data = e.data
                console.log('test.html 接收到的消息:',data)
                parent.postMessage('我已经接收到消息了!',e.origin)
            })
        </script>
    </body>
</html>

安全性: postMessage 采用的是 双向安全机制 。发送方发送数据时,会确认接收方的源,而监听方监听到 message 事件后,也可以用 event.origin 判断是否来自于正确可靠的发送方

webSocket实现跨域

websocket是一中全双工通信协议,该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信
websocket的应用实例:https://segmentfault.com/a/11...

document.domain

对于主域相同跨子域的情况,可以通过设置 document.domain 来解决。具体做法是在 example.com/a.html 和 sub.example.com/b.html 两个文件分别加上 document.domain = example.com
然后通过a.html 文件创建一个iframe,去控制iframe的 window,从而进行交互


昵称不能随便取
297 声望13 粉丝

没有一个问题是多加一层抽象层解决不了的