クロスドメインは開発においてよく遭遇するシナリオであり、インタビューでもよく議論される質問でもあります。一般的なクロスドメイン ソリューションとその背後にある原則を習得すると、開発効率が向上するだけでなく、面接もより快適になります。
それでは今日は、フロントエンドの観点からクロスドメインの問題を解決するためのいくつかの一般的な方法についてお話しましょう。
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/ // 端口号不同
なぜクロスドメインなのか
いわゆる同一オリジン ポリシーは、実際にはブラウザのセキュリティ メカニズムであり、Web ページ内のネットワーク リクエストを制限して、同じソース (ドメイン名、プロトコルとポート番号は同じです)リソースの主な目的は、悪意のある Web サイトがスクリプトを通じて他の Web サイトから機密データを盗むのを防ぎ、ユーザーのプライバシーとセキュリティを保護することです。
ブラウザ側のスクリプト (js ファイル) が他のドメインのネットワーク リソースにアクセスすると、クロスドメインの問題が発生します。
クロスドメインの問題を解決する方法
1. プロキシ サーバー
プロキシサーバー
クロスドメイン問題を解決するために、同一オリジンポリシーはサーバーにアクセスするブラウザのみに限定されており、サーバーにアクセスするブラウザには制限がないという特徴を実際に把握しています。サーバーにアクセスするサーバー仲介としてサーバーはリクエスト転送機能を持っています。 具体的には、フロントエンドエンジニアが書いたWebページは、webpackなどのスキャフォールディングで構築されたプロキシサーバー上で動作し、フロントエンドWebページがブラウザ上でネットワークリクエストを開始すると、実際にリクエストが送信されます。プロキシ サーバーに送信すると、プロキシ サーバーはリクエストをターゲット サーバーに転送し、ターゲット サーバーから返された応答をクライアントに転送します。
プロキシ サーバーは、このプロセスで中継の役割を果たし、リクエストと応答を変更、フィルタリング、インターセプトして、特定の機能を実現します。フロントエンド Web ページはプロキシ サーバー上で実行されるため、クロスドメインの問題は発生しません。
それでは、プロキシ サーバーはオンライン環境や開発環境でどのようにリクエストを転送するのでしょうか?
1. オンライン環境
nginx は軽量で同時実行性の高い Web サーバーであり、イベント駆動型のクロスプラットフォームであり、ウィンドウと Linux の両方で構成できます。
開発中のクロスドメインの問題を解決するプロキシ サーバーとして機能する主な方法は、オンライン フロントエンド URL の実行ポートをリッスンし、リクエストが発生した後にリクエストを転送することです。特別なタグ
が含まれています。2. 開発環境
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': '/' } } } } };
1.
まず、空のフォルダー (英語名) をプロジェクト フォルダーとして使用し、npm init -y コマンドを使用して、プロジェクトをノード プロジェクト
npm init -y
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>
を作成します。プロジェクトのルート ディレクトリの .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'); });
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的原理是通过动态创建