跨域是前端开发中经常会遇到的问题,前端调用后台服务时,通常会遇到 No 'Access-Control-Allow-Origin' header is present on the requested resource的错误,这是因为浏览器的同源策略拒绝了我们的请求。
所谓同源是指,域名,协议,端口相同,浏览器执行一个脚本时同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
这个问题我们通常会使用CORS(跨源资源共享)或者JSONP去解决,这两种方法也是使用较多的方法,网上的文章对于这两种的介绍都很详细了。但对于使用Nginx解决跨域大多写的不太详细,这里主要想介绍一下Nginx解决跨域问题的方法。
Nginx是一个高性能的HTTP和反向代理服务器,由俄罗斯的伊戈尔·赛索耶夫开发,其特点是占有内存少,并发能力强。Nginx可以在这里下载到,解压后双击nginx.exe即可运行。
负载均衡
提到Nginx这里就想多讲一下它的负载均衡,Nginx可以将请求转发均匀转发至多台服务器上,通过部署多台相同服务的服务器,可以减轻服务器的压力。
首先我们需要使用nodeJs开启两个服务,代码相同,返回结果不同
const server = require('express')()
// 所有的get请求
server.get('*', (req, res) => {
res.end(`
<!DOCTYPE html>
<html lang="en">
<head><title>Hello</title></head>
<body>
<h1>这里是8080端口</h1>
</body>
</html>
`)
})
server.listen(8080, function () {
console.log('server start')
})
接下来对nginx/conf下的nginx.conf文件做如下修改:
#进程数可根据cpu数量调整
worker_processes 1;
events {
#连接数
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
# 连接超时时间,服务器会在这个时间过后关闭连接。
keepalive_timeout 10;
# gizp压缩
gzip on;
# srever模块配置是http模块中的一个子模块,用来定义一个虚拟访问主机
server {
listen 80; # 监听80端口
server_name localhost;
# 根路径指到index.html
location / {
root html;
index index.html index.htm;
}
# /a会被分发到myserver
location /a {
proxy_pass http://myserver; # 负载均衡名
proxy_set_header X-real-ip $remote_addr;
proxy_set_header Host $http_host;
}
# 重定向错误页面到/50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
#负载均衡
upstream myserver {
server 192.168.0.103:8080 weight=1 max_fails=2 fail_timeout=20s;
server 192.168.0.103:8081 weight=1 max_fails=2 fail_timeout=20s;
# ip_hash;
}
}
nginx -s reload 重新载入nginx配置, 访问 http://localhost/a 就可以看到结果.
# srever模块, 监听localhost的80端口
server {
listen 80; # 监听80端口
server_name localhost;
# localhost/a 的请求会被均匀分发到myserver
location /a {
# 负载均衡名(用于进行负载均衡的配置)
proxy_pass http://myserver;
# 设置用户真实ip否则获取到的都是nginx服务器的ip
proxy_set_header X-real-ip $remote_addr;
proxy_set_header Host $http_host;
}
}
# 这里是对负载均衡的配置
upstream imgserver {
server 192.168.0.103:8080 weight=1 max_fails=2 fail_timeout=20s;
server 192.168.0.103:8081 weight=1 max_fails=2 fail_timeout=20s;
# ip_hash;
# ip_hash将某个ip的访问都定向到同一台服务器上,可以解决session问题
# 但ip_hash是有弊端的(具体百度)
# 为了解决ip_hash的一些问题,可以使用upstream_hash这个第三方模块
}
server后是两个服务的地址,以及参数设置, weight(权重), max_fails(最大失败次数), fail_timeout(请求失败暂停时间后重新请求).
解决前端跨域
回归正题, 修改nginx.conf文件如下:
#进程, 可更具cpu数量调整
worker_processes 1;
events {
#连接数
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#连接超时时间,服务器会在这个时间过后关闭连接。
keepalive_timeout 10;
# gizp压缩
gzip on;
# 直接请求nginx也是会报跨域错误的这里设置允许跨域
# 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
# srever模块配置是http模块中的一个子模块,用来定义一个虚拟访问主机
server {
listen 80;
server_name localhost;
# 根路径指到index.html
location / {
root html;
index index.html index.htm;
}
# localhost/api 的请求会被转发到192.168.0.103:8080
location /api {
rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前缀, 否则会出现404
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.0.103:8080; # 转发地址
}
# 重定向错误页面到/50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
nginx -s reload 重新载入nginx配置.
接下来配置本地服务器并启动
const server = require('express')()
var bodyParser = require('body-parser')
server.use(bodyParser())
server.post('/a', (req, res) => {
console.log(req.body)
let a = {
'json': 1
}
res.json(a)
})
server.listen(8080, function () {
console.log('server start')
})
前端代码
$.ajax({
url: "http://localhost/api/a",
type : "post",
data: {
CITY: '深圳'
},
success : function(data) {
alert( "Data: " + JSON.stringify(data) );
}
});
可以看到正确弹出了'Data: {json: 1}', 后台也成功输出了{CITY: 深圳}.
对于线上请求只需要将proxy_pass http://192.168.0.103:8080; 改为
proxy_pass http://目标地址; 即可.