32
头图

Preface

Hi everyone, this is Lin Sanxin. most difficult knowledge points in the most easy-to-understand is my motto, and based on the premise of advanced is my original intention.

When we do the front-end, it is necessary to connect the interface with the back-end, but many students may have overlooked a problem that may occur during the docking process- cross-domain , then what is cross-domain? Why is it cross-domain? How can it be solved?

截屏2021-10-01 上午7.16.06.png

Why cross domain?

image.png

Why is there a cross-domain problem? Then you have to talk about the browser’s same-origin policy , which stipulates that the protocol number-domain name-port number must be the same as to comply with the same-origin policy

截屏2021-10-01 上午8.50.11.png

If there is a not the same, there will be cross-domain problems. The consequences of homology policy are:

  • 1. LocalStorge、SessionStorge、Cookie cannot be accessed across domains
  • 2. DOM nodes cannot be operated across domains
  • 3. Ajax request cannot be cross-domain request

Note: One IP can register multiple different domain names, that is, multiple domain names may point to the same IP. Even so, they do not comply with the same-origin policy

截屏2021-10-01 上午9.02.55.png

The timing of cross-domain?

When did the cross-domain happen? I have tested many classmates and got two answers

  • 1. As soon as the request was sent, it was blocked by the browser's cross-domain error report (most people answered)
  • 2. The request is sent to the back-end, and the back-end returns data. When the browser receives the back-end data, it is blocked by the browser's cross-domain error report.

Which one is it? We can verify that we first npm i nodemon -g , then create a index.js , and then start a node service at nodemon index

// index.js  http://127.0.0.1:8000

const http = require('http');

const port = 8000;

http.createServer(function (req, res) {
    const { query } = urllib.parse(req.url, true);
    console.log(query.name)
    console.log('到后端喽')
    res.end(JSON.stringify('林三心'));
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

Create another index.html to write the front-end request code. Let’s write a simple AJAX request.

// index.html  http://127.0.0.1:5500/index.html
<script>
    //步骤一:创建异步对象
    var ajax = new XMLHttpRequest();
    //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
    ajax.open('get', 'http://127.0.0.1:8000?name=前端过来的林三心');
    //步骤三:发送请求
    ajax.send();
    //步骤四:注册事件 onreadystatechange 状态改变就会调用
    ajax.onreadystatechange = function () {
        if (ajax.readyState == 4 && ajax.status == 200) {
            //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
            console.log(ajax.responseText);//输入相应的内容
        }
    }

</script>

截屏2021-10-01 下午1.37.01.png

In the end, the front end did report an error across domains. But this is not the result. If we want to know which answer it is, the key is to see if there is any output from the back-end node service, which is clear at a glance. Therefore, answer 2 is correct.

截屏2021-10-01 下午1.38.52.png

截屏2021-10-01 下午1.41.51.png

Same-domain situation && cross-domain situation?

I mentioned the same-origin policy , which satisfies the protocol number-domain name-port number. The three are the same, which means that same domain. On the contrary, cross-domain will cause you to consolidate look at the same domain and cross domain understanding of 161ac1d2a6a93a!

截屏2021-10-01 上午9.24.38.png

Solution to cross-domain

Cross-domain is actually a long-standing problem, and there are many corresponding solutions. Let's read on together! ! !

JSONP

As we said earlier, because of the browser same-origin policy , there is the cross-domain problem . Is there anything that is not bound cross-domain problem In fact, yes, the following three tags load resource path is not restricted

  • 1. Script tag: <script src="load resource path"></script>
  • 2. Link tag: <link herf="load resource path"></link>
  • tag: 161ac1d2a6aa79 <img src="load resource path"></img>

The JSONP is the use of script of src load unfettered, so that you can have from the ability to get data of different domains. But JSONP needs front-end and back-end cooperation to achieve the final cross-domain data acquisition.

In layman's terms, JSONP is: use the src of the script to send the request, pass a method name callback to the backend, the backend gets the method name, and splices the required data into a new string callback (so Data required) and sent to the front end. After the front end receives this string, it will automatically execute the method callback (required data). The old rules, first upload the picture, and then the code.

截屏2021-10-01 下午1.22.08.png

backend code

// index.js  http://127.0.0.1:8000

const http = require('http');
const urllib = require('url');

const port = 8000;

http.createServer(function (req, res) {
    const { query } = urllib.parse(req.url, true);
    if (query && query.callback) {
        const { name, age, callback } = query
        const person = `${name}今年${age}岁啦!!!`
        const str = `${callback}(${JSON.stringify(person)})` // 拼成callback(data)
        res.end(str);
    } else {
        res.end(JSON.stringify('没东西啊你'));
    }
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

front-end code

// index.html  http://127.0.0.1:5500/index.html

    const jsonp = (url, params, cbName) => {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script')
            window[cbName] = (data) => {
                resolve(data)
                document.body.removeChild(script)
            }
            params = { ...params, callback: cbName }
            const arr = Object.keys(params).map(key => `${key}=${params[key]}`)
            script.src = `${url}?${arr.join('&')}`
            document.body.appendChild(script)
        })
    }

    jsonp('http://127.0.0.1:8000', { name: '林三心', age: 23 }, 'callback').then(data => {
        console.log(data) // 林三心今年23岁啦!!!
    })

截屏2021-10-01 下午1.47.29.png

The disadvantage of JSONP is that it requires front-end and back-end cooperation, and only supports the get request method

WebSocket

What is WebSocket? Actually I don't understand much, but I don't copy the MDN information directly like others, because I believe that everyone can't understand it after copying it.

I understand that WebSocket is a protocol (the same level as http, both protocols), and it can carry out cross-domain communication, why does it support cross-domain communication? I found an article here Why can WebSocket cross domains? , it’s pretty good

截屏2021-10-01 下午10.02.39.png

backend code

Install npm i ws

// index.js  http://127.0.0.1:8000
const Websocket = require('ws');

const port = 8000;
const ws = new Websocket.Server({ port })
ws.on('connection', (obj) => {
    obj.on('message', (data) => {
        data = JSON.parse(data.toString())
        const { name, age } = data
        obj.send(`${name}今年${age}岁啦!!!`)
    })
})

front-end code

// index.html  http://127.0.0.1:5500/index.html


    function myWebsocket(url, params) {
        return new Promise((resolve, reject) => {
            const socket = new WebSocket(url)
            socket.onopen = () => {
                socket.send(JSON.stringify(params))
            }
            socket.onmessage = (e) => {
                resolve(e.data)
            }
        })
    }
    myWebsocket('ws://127.0.0.1:8000', { name: '林三心', age: 23 }).then(data => {
        console.log(data) // 林三心今年23岁啦!!!
    })

result of 161ac1d2a6ad51 is as follows

截屏2021-10-01 下午1.47.29.png

Cors

Cors, the full name is Cross-Origin Resource Sharing , which means cross-domain resource sharing. Cors is generally turned on by the backend. Once turned on, the frontend can access the backend across domains.

Why is Cors enabled on the back end, and the front end can request the back end across domains? My understanding is: the front-end cross-domain access to the back-end, the back-end opens Cors, and sends Access-Control-Allow-Origin: domain name field to the front-end (actually more than one), and the front-end browser judges Access-Control-Allow-Origin the same as the front-end domain name. , The browser will not implement cross-domain interception, thus solving the cross-domain problem.

截屏2021-10-01 下午6.41.11.png

backend code

// index.js  http://127.0.0.1:8000

const http = require('http');
const urllib = require('url');

const port = 8000;

http.createServer(function (req, res) {
    // 开启Cors
    res.writeHead(200, {
        //设置允许跨域的域名,也可设置*允许所有域名
        'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
        //跨域允许的请求方法,也可设置*允许所有方法
        "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
        //允许的header类型
        'Access-Control-Allow-Headers': 'Content-Type'
    })
    const { query: { name, age } } = urllib.parse(req.url, true);
    res.end(`${name}今年${age}岁啦!!!`);
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

front-end code

// index.html  http://127.0.0.1:5500/index.html
    //步骤一:创建异步对象
    var ajax = new XMLHttpRequest();
    //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
    ajax.open('get', 'http://127.0.0.1:8000?name=林三心&age=23');
    //步骤三:发送请求
    ajax.send();
    //步骤四:注册事件 onreadystatechange 状态改变就会调用
    ajax.onreadystatechange = function () {
        if (ajax.readyState == 4 && ajax.status == 200) {
            //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
            console.log(ajax.responseText);//输入相应的内容
        }
    }

results of 161ac1d2a6aecf are as follows

截屏2021-10-01 下午1.47.29.png

截屏2021-10-01 下午7.10.57.png

Node interface proxy

Back to same-origin policy , the same-origin policy is only a browser policy, it is not restricted to the backend, that is, front-end-backend will be restricted by the same-origin policy, but backend-backend It will not be restricted, so you can use the Node interface proxy to first access the backend 1 where Cors has been set up, and then let the backend 1 access the backend 2 to get the data to the backend 1, and the backend 1 transmits the data to the frontend

截屏2021-10-01 下午8.46.28.png

backend 2 code

// index.js  http://127.0.0.1:8000

const http = require('http');
const urllib = require('url');

const port = 8000;

http.createServer(function (req, res) {
    console.log(888)
    const { query: { name, age } } = urllib.parse(req.url, true);
    res.end(`${name}今年${age}岁啦!!!`)
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

Create a index2.js and nodmeon index2.js

backend 1 code

// index2.js  http://127.0.0.1:8888

const http = require('http');
const urllib = require('url');
const querystring = require('querystring');
const port = 8888;

http.createServer(function (req, res) {
    // 开启Cors
    res.writeHead(200, {
        //设置允许跨域的域名,也可设置*允许所有域名
        'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
        //跨域允许的请求方法,也可设置*允许所有方法
        "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
        //允许的header类型
        'Access-Control-Allow-Headers': 'Content-Type'
    })
    const { query } = urllib.parse(req.url, true);
    const { methods = 'GET', headers } = req
    const proxyReq = http.request({
        host: '127.0.0.1',
        port: '8000',
        path: `/?${querystring.stringify(query)}`,
        methods,
        headers
    }, proxyRes => {
        proxyRes.on('data', chunk => {
            console.log(chunk.toString())
            res.end(chunk.toString())
        })
    }).end()
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

front-end code

// index.html  http://127.0.0.1:5500

//步骤一:创建异步对象
    var ajax = new XMLHttpRequest();
    //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
    ajax.open('get', 'http://127.0.0.1:8888?name=林三心&age=23');
    //步骤三:发送请求
    ajax.send();
    //步骤四:注册事件 onreadystatechange 状态改变就会调用
    ajax.onreadystatechange = function () {
        if (ajax.readyState == 4 && ajax.status == 200) {
            //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
            console.log(ajax.responseText);//输入相应的内容
        }
    }

result of 161ac1d2a6b0ce is as follows

截屏2021-10-01 下午1.47.29.png

Nginx

In fact, Nginx Node interface proxy are the same thing, but Nginx does not need us to build an intermediate service.

截屏2021-10-01 下午8.47.40.png

Download nginx first, and then modify the nginx.conf in the nginx directory as follows:

    server{
        listen 8888;
        server_name  127.0.0.1;
 
        location /{
            proxy_pass 127.0.0.1:8000;
        }
    }

Finally, start nginx through the command line nginx -s reload

backend code

// index.js  http://127.0.0.1:8000

const http = require('http');
const urllib = require('url');

const port = 8000;

http.createServer(function (req, res) {
    const { query: { name, age } } = urllib.parse(req.url, true);
    res.end(`${name}今年${age}岁啦!!!`);
}).listen(port, function () {
    console.log('server is listening on port ' + port);
})

front-end code

// index.html  http://127.0.0.1:5500

//步骤一:创建异步对象
    var ajax = new XMLHttpRequest();
    //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
    ajax.open('get', 'http://127.0.0.1:8888?name=林三心&age=23');
    //步骤三:发送请求
    ajax.send();
    //步骤四:注册事件 onreadystatechange 状态改变就会调用
    ajax.onreadystatechange = function () {
        if (ajax.readyState == 4 && ajax.status == 200) {
            //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
            console.log(ajax.responseText);//输入相应的内容
        }
    }

results of 161ac1d2a6b288 are as follows

截屏2021-10-01 下午1.47.29.png

postMessage

Scene: http://127.0.0.1:5500/index.html page using iframes. Tag embedded in a http://127.0.0.1:5555/index.html page

Although these two pages exist in one page, they need iframe tags to be nested. Communication between these two pages is impossible because their port numbers are different. According to the same-origin policy, between them There is a cross-domain problem

What should we do? Use postMessage to make these two pages communicate

截屏2021-10-01 下午9.28.53.png

// http:127.0.0.1:5500/index.html

<body>
    <iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
    document.getElementById('frame').onload = function () {
        this.contentWindow.postMessage({ name: '林三心', age: 23 }, 'http://127.0.0.1:5555')
        window.onmessage = function (e) {
            console.log(e.data) // 林三心今年23岁啦!!!
        }
    }
</script>
// http://127.0.0.1:5555/index.html

<script>
        window.onmessage = function (e) {
            const { data: { name, age }, origin } = e
            e.source.postMessage(`${name}今年${age}岁啦!!!`, origin)
        }
</script>

document.domain && iframe

Scenario: Communication between a.sanxin.com/index.html and b.sanxin.com/index.html

In fact, the above two cannot communicate under normal circumstances, because their domain names are different and belong to cross-domain communication.

then what should we do? In fact, they have one thing in common, that is, their second-level domain names are sanxin.com , which allows them to communicate document.domain && iframe

截屏2021-10-01 下午9.58.55.png

Since this rookie does not have a server for the time being, we temporarily use the local to simulate

// http://127.0.0.1:5500/index.html

<body>
    <iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
    document.domain = '127.0.0.1'
    document.getElementById('frame').onload = function () {
        console.log(this.contentWindow.data) // 林三心今年23岁啦!!!
    }
</script>
// http://127.0.0.1:5555/index.html

 <script>
        // window.name="林三心今年23岁啦!!!"
        document.domain = '127.0.0.1'
        var data = '林三心今年23岁啦!!!';
</script>

results of 161ac1d2a6b4cf are as follows

截屏2021-10-01 下午1.47.29.png

Concluding remarks

image.png


Sunshine_Lin
2.1k 声望7.1k 粉丝