前端跨域方案看这篇就够了


前言

为了加快请求响应时间,服务器进行分布式布局,将服务器分为:
web服务器:用于处理静态资源
data服务器:业务逻辑和数据分析
图片服务器

由于浏览器的同源策略,同源策略认为以下三者都一样就是同源,只要有一个不同就是跨域:

  • 协议
  • 域名
  • 端口号

导致需要用到跨域请求。

跨域解决的方法

1.JSONP

使用不存在跨域请求的限制的html标签发送get请求,比如以下标签:
-script
-img
-link
-iframe

jsonp实现:

服务器:

let express = require('express'),
  app = express();

app.listen(8001, () => {
  console.log("OK!");
})

app.get('/list', (req, res) => {
  let { callback = Function.prototype } = req.query;
  let data = {
    code: 0,
    message: "珠峰培训"
  };
  res.send(`${callback}(${JSON.stringify(data)})`)
})

客户端:

<!DOCTYPE html>
<html>
<head>
	<title>GoJSONP</title>
</head>

<body>
<script type="text/javascript">
	function jsonhandle(data){
		alert("age:" + data.age + "name:" + data.name);
	}
</script>

<script crossorigin="anonymous" integrity="sha384-rY/jv8mMhqDabXSo+UCggqKtdmBfd3qC2/KvyTDNQ6PcUJXaxK1tMepoQda4g5vB" src="https://lib.baomitu.com/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
	$(document).ready(function(){
		var url = "http://127.0.0.1:8001/list?callback=jsonhandle";
		var obj = $('<script><\/script>');
		obj.attr("src",url);
		$("body").append(obj);
	});
</script>
</body>
</html>

结果:
在这里插入图片描述

问题:JSONP只能支持GET请求。

2.CORS跨域资源共享

做法:

1.客户端(发送ajax/fetch请求)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script src = "./node_modules/_axios@0.21.1@axios/dist/axios.js"></script>
  <script>
    axios.get('http://127.0.0.1:3001/list')
      .then(result =>{
        console.log(result);
      })
  </script>

</body>

</html>

2.服务器端设置相关的头信息(需要处理options试探性请求)

let express = require('express'),
  app = express();

app.use('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*'); //这个表示任意域名都可以访问,这样写不能携带cookie了。为了保证安全
  //res.header('Access-Control-Allow-Origin', 'http://www.baidu.com'); //这样写,只有www.baidu.com 可以访问。
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
  res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//设置方法

  if (req.method == 'OPTIONS') {
    res.send(200); // 意思是,在正常的请求之前,会发送一个验证,是否可以请求。
  }
  else {
    next();
  }
});

app.listen(3001, () => {
  console.log("OK!");
})

app.get('/list', (req, res) => {
  let data = {
    code: 200,
    message: "我是服务器的数据!"
  };
  res.send(data)
})

3.http proxy => webpack webpack-dev-server

我们配置如下:
webpack.config.js

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "production",
  entry: './fun.js',
  output: { // 输出配置
    filename: './built.js', // 输出文件名
    path: resolve(__dirname, 'build/js') // 输出文件路径配置
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './please.html',
      filename: 'index.html'
    })
  ],
  devServer: {
    port: 3000,
    progress: true,
    contentBase: './build',
    open: true
    // proxy: {
    //   '/': {
    //     target: 'https://www.baidu.com/',
    //     changeOrigin: true
    //   }
    // }
  }
}

fun.js如下:

import axios from "axios"

axios.get('https://www.baidu.com/').then(res => {
  console.log(res);
})

打开webpack内置的服务器: npx webpack-dev-server。

会发生跨域,那么我们需要把devServer上的proxy注释去掉。就可以了。

这是Node中间件 代理实现的!

就是用Node来搭了一个代理服务器帮忙转发客户端的请求??毕竟服务器和服务器不存在跨域。

如果要发行呢??毕竟devServer 是方便我们调试。那么就需要用node来做代理了。

4.nginx反向代理

不需要前端干啥。其实就是nginx帮我们加了Access-Control-Allow-Origin的请求头啦。

5.postMessage(跟Worker很像)

我们先看看postMessage由谁来调用:

语法

otherWindow.postMessage(message, targetOrigin, [transfer]);
  • otherWindow

    其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames

另外一种说法是:

otherWindow

对将接收消息的窗口的引用。获得此类引用的方法包括:

  • Window.open (生成一个新窗口然后引用它),
  • Window.opener (引用产生这个的窗口,返回打开当前窗口的那个窗口的引用,例如:在window A中打开了window B,B.opener 返回 A.),
  • HTMLIFrameElement.contentWindow<iframe>从其父窗口引用嵌入式),
  • Window.parent(从嵌入式内部引用父窗口<iframe>
  • Window.frames +索引值(命名或数字)。

或者看看MDN上描述:

window.postMessage MDN

otherWindow.postMessage(message, targetOrigin, [transfer])
otherWindow: A reference to another window(发送方的引用)

message:Data to be sent to the other window.(要发送到接受方的数据)

targetOrigin:Specifies what the origin of otherWindow must be for the event to be dispatched(接收方的 源,还有必须要有监听message事件)

使用:

我们先建立两个不同源的服务器:

let express = require('express'),
  app = express();

app.use(express.static('./'));

app.listen(1001, () => {
  console.log("port of 1001 OK!");
})
let express = require('express'),
  app = express();

app.use(express.static('./'));

app.listen(1002, () => {
  console.log("port of 1002 OK!");
})

1001.html,这个文件跑在域名为127.0.0.1:1001的服务器上,用于向域名为127.0.0.1:1002的服务器发送数据:

<!DOCTYPE html>
<html lan上g="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
fuwfuq
<body>
  <h1>server1</h1>
  <iframe id="myframe" src="http://127.0.0.1:1002/1002.html" frameborder="0"></iframe>

  <script>
    myframe.onload = function () { //注意要onload之后,才可以用postMessage
      let iframeWindow = myframe.contentWindow;
      iframeWindow.postMessage("宁静致远", "http://127.0.0.1:1002/1002.html");
    }

    //监听B传递的信息
    window.onmessage = function (ev) {
      console.log(ev.data);
    }
  </script>

</body>

</html>

这里有个需要注意的问题,我们 发送方的引用,即调用postMessage() 那个引用的源要与接受方的源即postMessage()的第二的参数 要同源。

1002.html,这个文件跑在域名为127.0.0.1:1002的服务器上,用于向域名为127.0.0.1:1001的服务器发送和接收数据:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <h1>server2</h1>
  <script>
    //监听A发送过来的信息
    window.onmessage = function (ev) {
      console.log("server2 recevice message");
      ev.source.postMessage("recevice message", ev.origin);
    }
  </script>
</body>

</html>

结果:
在这里插入图片描述
这种方法本质是在一个页面上通过iframe标签打开不同源的其它页面,通过主页面与iframe交流的方法postMessage进行数据的交流。可以进行get和post请求。

6.WebSocket协议跨域


总结

前端跨域方案可以分为4个方向:

  1. 通过没有同源策略限制的Html标签实现数据请求。
  2. 在客户端建一个服务器代理。
  3. 请求加上access-control-change- 请求头。
  4. 使用websocket协议。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前端页面需要向不同域名的服务器发送请求时,由于浏览器的同源策略限制,会导致跨域请求失败。为了解决这个问题,可以通过配置nginx来实现前端跨域。 下面是一种常见的nginx配置前端跨域的方法: 1. 打开nginx的配置文件,一般位于`/etc/nginx/nginx.conf`或`/usr/local/nginx/conf/nginx.conf`。 2. 在http块内添加以下配置: ``` http { ... server { ... location / { # 允许指定的域名进行跨域请求 add_header Access-Control-Allow-Origin *; # 允许指定的请求方法进行跨域请求 add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; # 允许指定的请求头进行跨域请求 add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # 允许携带cookie进行跨域请求 add_header Access-Control-Allow-Credentials true; # 预检请求的有效期 add_header Access-Control-Max-Age 3600; ... } ... } ... } ``` 以上配置中,`add_header`指令用于添加响应头信息,实现跨域请求。其中: - `Access-Control-Allow-Origin`指定允许跨域请求的域名,可以使用通配符`*`表示允许所有域名。 - `Access-Control-Allow-Methods`指定允许的请求方法。 - `Access-Control-Allow-Headers`指定允许的请求头。 - `Access-Control-Allow-Credentials`指定是否允许携带cookie进行跨域请求。 - `Access-Control-Max-Age`指定预检请求的有效期,单位为秒。 配置完成后,保存并重启nginx服务,使配置生效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值