前几天朋友在做一个Node项目的时候遇到了一个问题,他使用的Node项目在测试环境都已经测试完成,没有任何问题,但是当部署到生成环境的时候,发现涉及到微信相关接口的调用总是请求超时,调用不成功。刚开始还以为是微信白名单之类的限制或者是微信公众号后台未配置相关的js接口安全域名或者业务域名导致。但是这些情况请求应该都是能够发送成功,然后会给一些错误提示,而不是直接请求超时的。
对于运维不太了解以及对于公司服务器不太了解的情况,他是一头雾水,后来他询问我是否了解相应的情况。在我了解情况之后,对于运维一窍不通的我也是一脸茫然。(看来多了解一些运维知识,以及横向多了解一些服务器的情况还是很重要的,而不只是做一个前端小朋友)
后来在跟他们给公司相应的运维同学沟通的过程中了解到,他们公司的生产服务器是不能直接访问外网的,是通过Squid代理服务器来进行外网请求的访问。
至于Squid,之前没有接触过。未接触过、感兴趣的同学可以自行去了解学习下。
当了解到服务器是通过代理进行相关外网请求的情况之后,我大概搜索了下Node进行相应的代理请求的方法。看到一篇浅笑同学NodeJS中配置请求代理服务器的文章之后做了简单的尝试:
使用 curl 我们可以在服务器上很快的看到代理服务器是否好使。
curl -x "http://8.8.8.8:80" https://www.lilnong.top/cors/sf4"
。 -x 是设置代理服务器地址, x 是小写哦。别写错,大写的是设置请求方式 -X POST。
首先尝试了文中提到的curl的方式进行验证,请求是OK的。说明代理服务器肯定没问题。之后就是处理程序中的代理问题。由于只是为了验证该方式是否可行,所以写了一个简单的node服务,然后通过nginx配置代理映射到该服务,然后进行请求验证。
// 代理 HTTP 请求方法
const http = require("http"); // Node.js自带的 http 模块
const axios = require('axios');
axios.defaults.timeout = 15000;
const url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxxxx&secret=xxxxx'
function onRequest(request, response) {
console.log("Request received.");
// 配置代理服务器信息
const proxy = {
host: "xx.xx.xx.xx", //代理服务器地址
port: 8080 // 代理服务器端口
};
// https://github.com/axios/axios#request-config
axios.get(url,{proxy:proxy})
.then((res) => {
console.log('res: ', res.data)
// 当收到请求时,使用 response.writeHead() 函数发送一个HTTP状态200和HTTP头的内容类型(content-type)
response.writeHead(200, {"Content-Type": "text/plain"});
// 使用 response.write() 函数在HTTP相应主体中发送文本“Hello World"。
response.write("Hello World");
// 最后,我们调用 response.end() 完成响应。
response.end();
})
.catch((error) => {
// console.log(error)
console.log(error.toJSON())
// 当收到请求时,使用 response.writeHead() 函数发送一个HTTP状态200和HTTP头的内容类型(content-type)
response.writeHead(200, {"Content-Type": "text/plain"});
// 使用 response.write() 函数在HTTP相应主体中发送文本“Hello World"。
response.write("error");
// 最后,我们调用 response.end() 完成响应。
response.end();
})
}
http.createServer(onRequest).listen(8888);
如文中所述,通过axios设置代理,该方法在请求https相应的接口的时候会返回501的情况。
// 代理 HTTPS 请求方法
const http = require("http");
const tunnel = require("tunnel");
const axios = require('axios-https-proxy-fix'); // 使用 axios-https-proxy-fix 版本来代替 axios。
axios.defaults.timeout = 15000;
const url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxx&secret=xxxxx'
function onRequest(request, response) {
console.log("Request received.");
axios(url,{
proxy: false,
// 使用 tunnel 隧道来处理。
httpsAgent: tunnel.httpsOverHttp({proxy:{
host: 'xx.xx.xx.xx',//代理服务器域名或者ip
port: 8080 //代理服务器端口
}})
})
.then((res) => {
console.log('res: ', res.data)
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
})
.catch((error) => {
console.log(error)
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("error");
response.end();
})
}
http.createServer(onRequest).listen(8888);
尝试了以上两种方式,确实能够请求成功了!文中提到的 request 方式并未进行验证。
request({
url:'https://www.lilnong.top/cors/sf3',
proxy: 'http://8.8.8.8:80'//代理服务器地址
// "http://" + proxyUser + ":" + proxyPass + "@" + proxyHost + ":" + proxyPort;
}, function (error, response, body) {
console.log('body:', body);
});
不过在本文提到的Node项目中并未使用axios进行请求,而是使用node-fetch
以及isomorphic-fetch
来进行请求的发送。因为不想修改原有代码的请求方式,所以并未把请求方式替换为axios来解决问题。
进而又查询了node-fetch
以及isomorphic-fetch
进行代理请求的相关设置。最后的解决方式如下:
// 通过 https-proxy-agent 来代理https请求,http-proxy-agent 来代理http请求
const HttpProxyAgent = require('https-proxy-agent');
const fetch = require("node-fetch"); // isomorphic-fetch 也是相同的设置
const proxyIp = 'xx.xx.xx.xx'; // 可以配置到配置文件里,根据不同环境的需要进行设置
const proxyPort = '8080';
const fetchParam = {
method: 'GET',
agent: new HttpProxyAgent("http://" + proxyIp + ":" + proxyPort)
}
fetch('请求的url', fetchParam).then((response) => {
console.log(response)
}, (e) => {
console.log(e)
})
当然为了避免在任何发送请求的地方都需要做这样的设置,可以封装一个请求的工具方法,把代理逻辑写在该工具方法里,在需要使用fetch的地方直接引入使用即可。
完。