上篇 JSONP 的文章里提到过利用 Nginx 也可以解决跨域问题。趁着自己以前没有接触过 Nginx,熟悉了一下,顺带写了一个非常非常简单的 demo 实验下。
正向代理和反向代理
提到代理,肯定要说一下这两个的区别。
举个正向代理的例子:我打球累了走不动了,找看球的小朋友帮我去旁边的商店买瓶水。商店老板是不知道到底是谁需要喝水的,隐藏了客户端。当然,小朋友可以告诉老板就是那个打球像蔡徐坤的人要喝水。还有,VPN 就是正向代理。
反向代理的例子:我打球累了,找看球的小朋友要瓶水喝(当然我肯定会给钱的:D)。我不需要知道小朋友的水是从旁边的商店还是两公里外的超市买的。隐藏了服务端。还有,我们连好了 VPN 访问谷歌的时候,浏览的那些页面,我们是不会知道具体是哪台服务器的资源。
具体步骤
- 服务接口
既然请求,肯定需要先写一个服务接口,我们用 node 起一个服务:
// index.js
const http = require('http');
const fs = require('fs');
const url = require('url');
const server = http.createServer(function (req, res) {
if (req.url === '/favicon.ico') {
return;
}
const parseUrl = url.parse(req.url, true);
console.log('parseUrl', parseUrl.pathname)
if (parseUrl.pathname === '/api/getList') {
const list = {'a': 1, 'b': 2}
res.writeHead(200, {'content-Type':'text/html;charset=UTF-8'})
res.end(JSON.stringify(list))
}else {
res.write(`
port: 666
`)
res.end()
}
});
server.listen(666, function () {
console.log('server is starting on port 666');
});
我们来访问一下,可以拿到数据了。
- 测试页面
然后,我们写一个简单的 ajax 请求页面。你可以本地用http-server
启动访问下,可以发现请求跨域了:
<html>
<head>
<title></title>
</head>
<body>
<button onclick="sendAjax()">sendAjax</button>
<script type="text/javascript">
var sendAjax = () => {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:666/api/getList', true);
xhr.send();
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
}
</script>
</body>
</html>
- 安装 Nginx
这个时候,你可以通过设置响应头来允许跨域。或者用 Nginx 来解决这个问题了。首先肯定需要安装 Nginx。这个按照对应的平台安装就行了。
brew update
brew install nginx
nginx
nginx -s reload // 重启
- 配置
然后我们配置一下代理,这个意思就是我们请求中有 api 这样的就会代理到 http://127.0.0.1:666,所以我们只要访问 http://localhost:9999/api/getList 这个不跨域的接口,然后就会由服务器反向代理到 http://localhost:666/api/getList。
listen 9999;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /api/ {
proxy_pass http://127.0.0.1:666;
}
配置好之后我们需要重启一下 Nginx 服务。注意一点,重启时可能会报这么一个错误:
nginx: [error] open() "/usr/local/var/run/nginx.pid" failed (2: No such file or directory)
这是 sudo nginx -s stop
这个命令会删除 pid 文件,可以执行 sudo nginx
重新添加这个文件。
- 测试结果
这个时候,我们不用绝对地址了,我们把ajax请求里面的接口换成相对地址:
// xhr.open('GET', 'http://localhost:666/api/getList', true);
xhr.open('GET', '/api/getList', true);
美滋滋,这就不跨域了呢。
当然,还可以更加真实一点,我们随便用一个域名测试一下。Nginx 重新配置下:
listen 80;
server_name yumingtest;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /api/ {
proxy_pass http://127.0.0.1:666;
}
然后在 hosts 文件里面添加这条:127.0.0.1 yumingtest.com
,重启下 Nginx。
这下是不是更加真实了。关于hosts 文件的作用,就是我们输入域名不是要需要经过DNS解析IP嘛,这个里面就存了一些。首先自动从 Hosts 文件中寻找对应的 IP 地址,一旦找到,系统会立即打开对应网页,如果没有找到, 则系统再会将网址提交 DNS 域名解析服务器进行 IP 地址的解析。
还有一个问题,关于 HTTP 502 状态码,我是把接口服务停了,于是就报 502了。
也不一定是网上说的什么连接超时 我们向服务器发送请求 由于服务器当前链接太多,导致服务器方面无法给于正常的响应,产生此类报错
那样。
(完)
2019.11.30 补充
上面 Hosts 文件那里说错了,输入域名后并不是先自动从 Hosts 文件中寻找对应的 IP 地址。而是浏览器先从浏览器的dns缓存中找,找不到再去 hosts文件中找 :)