几种常见的nodejs跨域解决方案

在我们使用ajax进行前后端数据交互的时候,经常会遇到一个跨域的报错信息:

Access to XMLHttpRequest at 'http://localhost:3000/cors' from origin 'null' has
been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on 
the requested resource.

为什么产生跨域?

这是因为浏览器的同源策略。这是浏览器最核心也最基本的安全功能,它阻止一个域上加载的脚本去获取或操作另一个域上的文档属性。(也就是说,受到请求的URL的域必须与当前web页面的域相同),如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

哪些情况会产生跨域?

  1. 端口不同
  2. 协议不同
  3. 域名不同
  4. 相同的网址和域名对应的IP不同 //localhost和127.0.0.1会生跨域

总结:端口不同,域名不同,协议不同都会出现跨域

同源策略限制以下几种行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM和JS对象无法获得
  • AJAX 请求不能发送

如何解决解决跨域?

在前端开发中,不可避免要出现跨域问题,下面提供四种方法供参考。

注意:一般出现跨域应该是前后端共同配置解决

方法一:服务器代理

基本原理:服务器之间的请求没有跨域问题。

  1. 首先前端请求自己的服务器。 通过第三方中间件cors,来解决前端和自己的服务器之间的跨域问题。
  2. 我方服务器发起对目标服务器发送服务器请求。
  3. 将网路请求的结果返回给前端
    服务器代理

前端代码:

let  url ='http://localhost:3000/cors';//请求自己的服务器
      $.get(url,(data)=>{
        console.log(data);//后端返回的数据
      })

server.js:

const cors = require('cors')
const axios =require('axios')
const express = require('express')
const path = require('path')
const app = express()
// 通过第三方中间件cors 来实现跨域问题
app.use(cors())//如果访问的是目标服务器,那么这里就相当于一个服务器代理

app.get('/cors',(req,res)=>{
  console.log('请求到了')
  let url ='http://ustbhuangyi.com/music/api/getDiscList?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&platform=yqq&hostUin=0&sin=0&ein=29&sortId=5&needNewCode=0&categoryId=10000000&rnd=0.2209329929181545'
  // 直接发起一个服务器请求
  axios.get(url)
  .then((data)=>{
    // console.log(data.data)
    res.send(data.data)
  })
})
app.listen(3000,()=>{
  console.log('服务器启动')
})

方法二:JSONP方式(前端常用)

核心:script标签的src属性不存在跨域,可以加载任何文件信息。

一般第三方接口可以使用此方法,也是服务器和客户端之间通信的常用方法。全平台浏览器都支持。

扫描二维码关注公众号,回复: 9778292 查看本文章

比如,需要返回一个淘宝推荐的数据接口

let url = https://suggest.taobao.com
/sug?code=utf-8&q=%E8%A1%A3%E6%9C%8D&_ksTS=1577354356112_350
&callback=taobao&k=1&area=c2c&bucketid=3//前端页面在调用接口时,需要以callback=回调函数名的形式

可以通过script标签实现跨域请求,然后再用json数据并执行回调函数

<script>
     function taobao(data){
         console.log(data);
     }
</script>

这个接口打印出来的数据如下图所示
接口数据
若项目使用vue写的,那么也可以用jsonp这个模块

api.js

import jsonp from 'jsonp'

const getData = () => {
  let url = 'https://suggest.taobao.com/sug?code=utf-8&q=%E8%A1%A3%E6%9C%8D&_ksTS=1577354356112_350'
  // param给后端传递函数名的字段 由后端确定的 不能随便写
  return new Promise((resolve, reject) => {
    jsonp(url,{param:'callback'},(err, data) => {
      if(err){
        reject(err)
      }else {
        resolve(data)
      }
    })
  })
}

index.vue

import { getData} from "api/api.js";

getData().then((res)=>{
	console.log(res) // 若是请求成功,得到数据
})

这个方法很简单但也有弊端:

  1. jsonp请求方式只能是get。使数据在传递的过程中毫无安全性可言,而且所能传输的数据长度也相当有限。
  2. 接口必须符合jsonp格式,否则采用此方法没意义。

方法三:cors设置请求的相应头字段

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

支持所有类型的HTTP请求,但浏览器IE10以下不支持),适合做ajax各种跨域请求。

后端代码:

var express = require('express');
var app = express();

// 中间件 解决跨域问题
function middleware(req, res, next) {
    console.log('这里是中间件')
    // 跨域处理
    res.header("Access-Control-Allow-Origin", "*");//设置允许跨域的域名,*代表允许任意域名跨域
    res.header("Access-Control-Allow-Headers", "X-Requested-With");//允许的header类型
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");//跨域允许的请求方式
    res.header("X-Powered-By", ' 3.2.1');
    res.header("Content-Type", "application/json;charset=utf-8");
    next(); // 执行下一个路由
}

//all是路由中指代所有的请求方式
app.all(middleware) //使用全局自定义中间件,放到起始位置

app.get('/test', function (req, res) {
    res.send(req.query);
})
app.listen(3000, function () {
    console.log('服务器启动');
})

用postman进行测试结果如下图所示:
在这里插入图片描述

方法四:WebSocket(H5新增特性)

WebSocket是HTML5一种新增的通信协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生websocket有兼容问题,虽然使用起来很简单,但是低版本浏览器使用不了。可以使用第三方库:socket.io 它很好的解决了兼容问题,而且使用方式也只是稍微复杂了一点。

前端代码:

<body>
 <input type="text" id='msg' ><button onclick="send()">send</button>
<script>
  // 与服务器实现连接
   var socket = io.connect('http://127.0.0.1:8081');
  //  创建一个自定义事件监听 事件名叫hehe 等待服务端触发
   socket.on('hehe',(data)=>{
     console.log('来自服务端的信息',data)
   })
   function send(){
     let msg = document.getElementById('msg').value 
     // 客户端触发服务端的自定义事件 事件名叫xixi 传递的数据是input的value值
     socket.emit('xixi',msg)
   }
</script>
</body>

server.js:

var express = require('express')
var app = express()
// 将socket服务器 和express 绑定到一起
var server = require('http').Server(app);
var io = require('socket.io')(server);

io.on('connection',(client)=>{
   console.log('客户端连接')
  //  触发客户端的自定义事件 hehe 参数是123
   client.emit('hehe',123)
  //  服务端创建一个叫xixi的自定义事件监听 等待前端触发
   client.on('xixi',(data)=>{
     console.log('来自客户端的消息',data)
   })
})

server.listen(8081,()=>{
  console.log('服务器启动')
});

方法五: vue-cli的devServer代理配置

如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。这个问题可以通过 vue.config.js 中的 devServer.proxy 选项来配置。vue cli: devServer.proxy

  1. 在项目根目录下新建vue.config.js文件

  2. 在module.exports内设置devServer来处理代理

    module.exports={
      devServer:{
        proxy:{    //配置代理服务器
          // 接口小暗号
          '/hehe':{
            target:'http://www.target.com', //要转发的目标网址
            changeOrigin:true,
            pathRewrite:{
              "^/hehe":''  //将路径中多余的暗号 删除
            }
          }
        }
      }
     }
    
  3. 获取接口的网络请求
    利用我们之前设置修改的接口小暗号,将请求发送至本地服务器。
    此时本地服务器接收的地址是:/hehe/music/api/123 所以不会产生跨域

    import  axios from '../utils/axios'
    let url ='/hehe/music/api/123'
    axios.get(url)
    
  4. 然后再通过本地服务器转发至目标服务器,在转发之前,本地服务器会把target目标地址:target:'http://www.target.com'加至接收地址url前:url ='http://www.target.com/hehe/music/api/123'

  5. 这时我们发现准备发送的地址和原地址不符,之间多了一个hehe标识符。所以我们需要pathRewrite将/hehe从地址中删去

使用场景

通过 devServer 设置的代理只适合在本地环境使用,即通过代理把请求发送到目标服务器上,如果发布到线上,则会提示 404 路径未找到

小结

还有一些解决跨域的方案,比如nginx代理跨域、 postMessage跨域、document.domain + iframe、location.hash + iframe、 window.name + iframe等等。

没有最好,只有最合适的,结合项目实际场景选择合适的跨域方案。

发布了3 篇原创文章 · 获赞 4 · 访问量 355

猜你喜欢

转载自blog.csdn.net/Pentoncos/article/details/104461034