Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,简单的说 Node.js 就是运行在服务端的 JavaScript。

不过本文要讲的解决跨域问题

例如:我们当前服务为http://localhost:8080/,现在我们有一个需求,如果我们请求/api,而这个api请求又和当前服务不是同源的,即涉及到跨域问题,相信很多开发者都有遇到这样的问题。

我之前有一篇博文《浅谈跨域》是讲述一些跨域问题的,其中讲到了在vue-cli项目中在webpack配置一下proxyTable

即可以实现在开发环境下进行跨域请求。

那生产环境依然涉及到跨域怎么办?

node代理服务!,用到的关键依赖是http-proxy-middleware

http-proxy-middleware

http-proxy-middleware是用于把请求代理转发到其他服务器的中间件。

  • 原来请求过程:本地服务——x(浏览器同源策略)——> api
  • 使用node代理:本地服务——(node代理同意该请求)——> Node代理 ——(脱离浏览器,无同源策略)——> api

node代理服务代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let proxy = require('http-proxy-middleware')
let express = require('express')

let app = express();

// 跨域配置
app.all('*', function (req, res, next) {
if (!req.get('Origin')) return next()
// 用 "*" 以同意所有请求源请求该服务
res.set('Access-Control-Allow-Origin', '*')
// 用 "*" 以同意所有请求方式 (get/post/patch...)
res.set('Access-Control-Allow-Methods', '*')
// 允许的请求头配置,可根据自己的要求填写更多
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type')
if (req.method === 'OPTIONS') return res.sendStatus(200)
next()
})

// 转发至目标(这里以http://XXX.xxx/api代替,应写具体)
app.use('/api', proxy({target: 'http://XXX.xxx/api', changeOrigin: true}));

app.listen(3000);

现在,我们利用express在3000端口启动了一个小型的服务器,利用了

1
app.use('/api', proxy({target: 'http://XXX.xxx/api', changeOrigin: true}));

这句话,使发到3000端口的/api请求转发到了3001端口。即请求http://localhost:3000/api相当于请求http://XXX.xxx/api

  • 本地服务——(node代理同意该请求)——> Node代理 ——(脱离浏览器,无同源策略)——> api

要知道,‘同源,跨域’为浏览器的安全策略,而脱离了浏览器的的node代理服务,已不再受该策略的束缚,实现跨域请求。

HTTPS

有时候我们还需要使node代理服务为HTTP通道,以满足开发需求,要知道,以HTTPS和HTTP之间的请求也是存在问题的。

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL

要实现从HTTP到HTTPS的改变 最重要的就是SSL证书

SSL证书的获取我就不多讲了,很多平台都有免费的,像阿里云,腾讯云等都可以之间申请免费的SSL证书,当然,您要是有钱大佬,也可以花钱购买。

很多平台拿到的证书文件可能没有直接说明哪些是用于Node服务的,其他都有区分

这里以腾讯云获取的证书为例

我们只需要用Nginx文件下的文件即可,其他平台也是一样获取Nginx服务项的文件

将其放到node服务目录下

则原来的node服务代码修改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let proxy = require('http-proxy-middleware')
let express = require('express')

let https = require("https"); //添加代码
let fs = require("fs"); //添加代码

const httpsOption = { //添加代码
key : fs.readFileSync('./nginx/xxxxxxxxx.key'),
cert: fs.readFileSync('./nginx/xxxxxxxxx.crt')
}

let app = express()

// 跨域配置
app.all('*', function (req, res, next) {
if (!req.get('Origin')) return next()
// 用 "*" 以同意所有请求源请求该服务
res.set('Access-Control-Allow-Origin', '*')
// 用 "*" 以同意所有请求方式 (get/post/patch...)
res.set('Access-Control-Allow-Methods', '*')
// 允许的请求头配置,可根据自己的要求填写更多
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type')
if (req.method === 'OPTIONS') return res.sendStatus(200)
next()
})

// 转发至目标(这里以http://XXX.xxx/api代替,应写具体)
app.use('/api', proxy({target: 'http://XXX.xxx/api', changeOrigin: true}));

app.listen(3000);

https.createServer(httpsOption, app).listen(444,() => { //添加代码
console.log(`HTTPS Server is running on: 444`)
})

注:为了不占用默认的443端口,选用444端口

这样,我们既可以请求http://localhost:3000/api,也可请求https://localhost:444/api,都是一样转而请求http://XXX.xxx/api