크로스 도메인은 개발 과정에서 자주 접하는 시나리오이자, 인터뷰에서도 자주 거론되는 질문이기도 합니다. 공통 도메인 간 솔루션과 그 뒤에 숨은 원칙을 익히면 개발 효율성이 향상될 뿐만 아니라 인터뷰에 더욱 편안해집니다.
그래서 오늘은 프런트엔드 관점에서 도메인 간 문제를 해결하는 몇 가지 일반적인 방법에 대해 이야기해 보겠습니다.
교차 도메인에 대해 이야기하기 전에 먼저 URL 구성을 살펴보겠습니다.
URL 구성에는 일반적으로 프로토콜, 호스트 이름, 포트 번호, 경로가 포함됩니다. , 쿼리 매개변수 및 앵커 여러 부분.
URL의 예는 다음과 같습니다.
https://www.example.com:8080/path/resource.html?page=1&sort=desc#header
위의 예에서:
● 프로토콜은 HTTPS입니다.
● 호스트 이름은 www.example.com
● 포트 번호는 8080입니다.
● 경로는 /path입니다. /resource.html
● 쿼리 매개변수는 page=1&sort=desc
● 앵커는 header
소위 크로스 도메인은 요청 URL의 프로토콜, 호스트 이름 및 포트 번호의 일부가 다른.
위 URL을 예로 들면 다음과 같은 작성 방식은 크로스 도메인으로 간주됩니다.
http://www.example.com:8080/ // 协议不同 https://www.example.a.com:8080/ // 主机名不同 https://www.example.com:8081/ // 端口号不同
실제로크로스 도메인 문제 발생은 제한적입니다. 브라우저의 동일 출처 정책에 따라.
所谓同源策略,其实是浏览器的一种安全机制,用于限制一个网页中的网络请求仅能够访问来自同一源(域名、协议和端口号均相同)的资源,主要目的是防止恶意网站通过脚本窃取其他网站的敏感数据,保障用户的隐私和安全。
브라우저 측 스크립트(js 파일)가 다른 도메인의 네트워크 리소스에 액세스하면 도메인 간 문제가 발생합니다.
앞서 언급했듯이 크로스 도메인 문제의 발생은 브라우저의 동일 출처 정책에 의해 제한되므로 크로스 도메인 문제를 해결하는 일반적인 솔루션은 실제로 브라우저를 중심으로 이루어집니다. :
우리의 일반적인 개발에서 도메인 간 문제를 해결하기 위해 가장 일반적으로 사용되는 솔루션은 프록시 서버를 사용하는 것입니다.
프록시 서버 크로스 도메인 문제를 해결하기 위해 동일 출처 정책이 브라우저의 서버 액세스에 의해서만 제한되고 서버의 서버 액세스에는 제한이 없다는 기능을 실제로 캡처합니다. 중간 서버로서 요청 전달 기능이 있습니다.
구체적으로 프런트엔드 엔지니어가 작성한 웹페이지는 webpack과 같은 스캐폴딩으로 구축된 프록시 서버에서 실행됩니다. 프런트엔드 웹페이지가 브라우저에서 네트워크 요청을 시작하면 해당 요청이 실제로 프록시 서버로 전송됩니다. , 프록시 서버 요청은 대상 서버로 전달되고 대상 서버에서 반환된 응답은 클라이언트로 전달됩니다. 프록시 서버는 이 프로세스에서 중계 역할을 하며 일부 특정 기능을 달성하기 위해 요청과 응답을 수정, 필터링 및 가로챌 수 있습니다.프런트엔드 웹페이지는 프록시 서버에서 실행되기 때문에 크로스도메인 문제가 없습니다.
그렇다면 프록시 서버는 온라인 환경과 개발 환경에서 어떻게 요청을 전달할까요?nginx를 역방향 프록시로 사용하여 프런트엔드 요청을 대상 인터페이스로 전달합니다.
nginx는 경량의 동시성 웹 서버, 이벤트 중심, 크로스 플랫폼이며 Windows와 Linux 모두에서 구성할 수 있습니다.개발 중 도메인 간 문제를 해결하기 위해 프록시 서버로 사용하는 주요 방법은 온라인 프런트 엔드 URL의 실행 중인 포트를 수신한 다음 특수 태그가 포함된 요청을 만난 후 요청을 전달하는 것입니다.
의 도움으로 달성됩니다. http-proxy-middleware 미들웨어 . http-proxy-middleware 미들웨어의 핵심은 http-proxy를 추가로 캡슐화한 것입니다.
다음은http-proxy-middleware를 사용하여 프로젝트에서 요청 전달 기능을 구현하는 샘플 코드입니다.
const { createProxyMiddleware } = require('http-proxy-middleware'); module.exports = { server: { proxy: { // 将 /api/* 的请求代理到 http://localhost:3000/* '/api': { target: 'http://localhost:3000', changeOrigin: true, pathRewrite: { '^/api': '/' } } } } };
우리는 기본 노드를 직접 사용하고 http-proxy 라이브러리를 사용하여 요청 전달을 구축할 수 있습니다. 기능 프록시 서버 데모, 관심 있는 친구가 직접 테스트하고 플레이할 수 있습니다. :
1. 먼저 프로젝트 폴더로 빈 폴더(영어 이름)를 만든 다음 npm init -y를 사용해야 합니다. 프로젝트를 설치하는 명령 프로젝트는 노드로 업그레이드되었습니다:
npm init -y
2. 그런 다음 프로젝트 루트 디렉터리에 index.html 파일을 만들어 교차 도메인 요청을 시작합니다.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>请求转发测试</title> </head> <body> <h1>请求转发测试</h1> <p id="message"></p> <script> fetch('/api/login') .then(response => response.text()) .then(data => { document.getElementById('message').textContent = data; }); </script> </body> </html>
3. 그런 다음 새 를 만듭니다. 서버측 코드를 작성하기 위한 프로젝트 루트 디렉터리 .js 파일의 인덱스입니다.
index.js 파일은 요청 전달 기능을 갖춘 프록시 서버를 구현하기 위한 핵심 파일입니다.
const http = require('http'); const httpProxy = require('http-proxy'); const fs = require('fs'); const path = require('path'); // 创建代理服务器实例 const proxy = httpProxy.createProxyServer({}); // 创建HTTP服务器 const server = http.createServer((req, res) => { if (req.url === '/' || req.url.endsWith('.html')) { // 读取HTML文件 const filename = path.join(__dirname, 'index.html'); fs.readFile(filename, 'utf8', (err, data) => { if (err) { res.writeHead(500); res.end('Error reading HTML file'); } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(data); } }); } else if (req.url.startsWith('/api')) { // 重写路径,替换跨域关键词 req.url = req.url.replace(/^\/api/, ''); // 将请求转发至目标服务器 proxy.web(req, res, { target: 'http://localhost:3000/', changeOrigin: true, }); } }); // 监听端口 server.listen(8080, () => { console.log('Server started on port 8080'); });
4. 그런 다음 도메인 간 액세스 테스트를 위해 대상 서버 target.js 파일의 내용을 작성합니다.
const http = require('http'); const server = http.createServer((req, res) => { if (req.url.startsWith('/login')) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('我是localhost主机3000端口下的方法,恭喜你访问成功!'); } else { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello, world!'); } }); server.listen(3000, () => { console.log('Target server is listening on port:3000'); })
5. 打开终端,输入启动目标服务器的命令:
node ./target.js //项目根目录下执行
6. 再开一个终端启动代理服务器,等待浏览器端发起请求就可以啦:
node ./index.js //项目根目录下执行
7. 最后在浏览器里访问http://localhost:8080, 打开控制台即可查看效果:
可以发现,浏览器network模块的网络请求确实是访问的8080端口的方法,但是我们的服务器默默的做了请求转发的功能,并将请求转发获取到的内容返回到了前端页面上。
其实http-proxy是对node内置库http的进一步封装,网络请求的核心部分还是使用http创建一个服务器对象去访问的。感兴趣的同学可以再读读http-proxy的源码~
除了代理服务器这种绕过浏览器同源策略的解决方式外,从前端的角度解决跨域问题还有如下一些常见的方法:
JSONP的原理是通过动态创建