跨域对于前端来说是一个老大难的问题,许多方法如jsonp
、document.domain + iframe
...都有或多或少的问题,一个最佳实践就是通过服务器nginx
做反向代理,但奈何不懂相关知识,就一直琢磨着使用 node.js
来做。
之前公司php写的接口,然后用node定义一样的路由,前端请求node的接口,然后通过Node在控制器中访问php的接口,这样确实能解决跨域问题,不过也是有缺点的,不能带上cookic等信息,不等同于反向代理;
事实上使用node是可以很容易构建本地的反向代理,使用 http-proxy-middleware 模块
假如目前有一个php提供的接口为 http://api.text.com/getdata 需要去代理
首先
npm i http-proxy-middleware --save //反向代理包
npm i cors --save //node跨域模块
然后:生成一个新的 express,app.js里面 删掉 index和user这两个路由,然后
var cors = require('cors'); var proxy = require('http-proxy-middleware'); var options = { target: 'http://api.text.com', // 目标主机,提供接口服务的域名 changeOrigin: true, // 需要虚拟主机站点 }; var exampleProxy = proxy(options); //开启代理功能,并加载配置
app.use(cors()); app.use(exampleProxy); //代理任意请求的路由,也可以改成代理固定的某些路由
启动express服务
前段代码:
const host = "http://127.0.0.1:3000";
$.ajax({
url: host+ "/getdata"
}).then(function(data){
console.log(data);
}).fail(function(error){
console.log(error);
});
目前为止,反向代理成功,本地电脑可以跨域访问远程服务接口
另外,附加上,自己写的一个,携带cookic 的转发:
roouter.js 文件
var express = require('express'); var router = express.Router(); var request = require("request"); /* GET home page. */ const Api = { GET(params){ params = params || {}; return new Promise(function(resolve,reject){ request({ method:"GET", url:params.url, qs:params.data, headers:params.headers },function (error, response, body) { if (!error) { resolve({response,body}); }else{ reject(error); } }); }) }, POST(params){ params = params || {}; return new Promise(function(resolve,reject){ request({ method:"POST", url:params.url, form:params.data, headers:params.headers },function (error, response, body) { if (!error) { resolve({response,body}); }else{ reject(error); } }); }) } } module.exports = function(opt){ opt = opt || {}; var target = opt.target || ''; var url = opt.url || "**"; router.all(url,function(req,res,next){ var url = target + req.baseUrl + req.path; var data = {}; var method = req.method; if(method =="GET"){ data = req.query; }else if(method == "POST"){ data = req.body; } let Cookie = req.header("Cookie"); let content = req.header('Content-type'); console.log("url:"+url,data,Cookie,content); data.headers = { "content-type": content, "Cookie":Cookie }; if(Api[req.method]){ Api[req.method]({ url:url, data:data }).then(function({response,body}){ try{ body = JSON.parse(body); }catch(e){ } // ************** 透传响应中的cookie ************** if (response.headers['set-cookie']) { res.setHeader("set-cookie", response.headers['set-cookie']); } // ************** 自定义cookie ************** //res.cookie('tc', 'test-cookie', {maxAge: 2 * 60 * 60 * 1000, httpOnly: true}); return res.status(response.statusCode).send(body); //return res.json(body); }).catch(function(e){ res.json(e); }) }else{ res.json({code:500,msg:"不支持的请求类型"}); } }) return router; };
index.js
var express = require('express'); var router = require("./routers"); var cors = require('cors'); var app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cors()); var config = { target:"http://xxxxxx" }; app.use("/",router(config)); app.listen("3000",function(error){ console.log("启动服务-----3000"); })
koa2 版本
const router = require('koa-router')() var request = require("request"); /* GET home page. */ const Api = { GET(params){ params = params || {}; return new Promise(function(resolve,reject){ request({ method:"GET", url:params.url, qs:params.data },function (error, response, body) { if (!error && response.statusCode == 200) { try{ body = JSON.parse(body); } catch(e){ } resolve(body); }else{ reject(error); } }); }) }, POST(params){ params = params || {}; return new Promise(function(resolve,reject){ request({ method:"POST", url:params.url, form:params.data },function (error, response, body) { if (!error && response.statusCode == 200) { try{ body = JSON.parse(body); } catch(e){ } resolve(body); }else{ reject(error); } }); }) } } module.exports = function(opt){ opt = opt || {}; var target = opt.target || ''; var url = opt.url || "**"; if(opt.prefix){ router.prefix(opt.prefix) } router.all(url,async function(ctx,next){ var url = target + ctx.path; var data = {}; if(ctx.method =="GET"){ data = ctx.query; }else if(ctx.method == "POST"){ data = ctx.request.body; } console.log("请求来源:"+url,data,ctx.method); if(Api[ctx.method]){ try{ ctx.body = await Api[ctx.method]({ url:url, data:data }); }catch(e){ ctx.body = e; } }else{ ctx.body = {code:500,msg:"不支持的请求类型"}; } }) return router; };
使用:
const proxy = require('./routers'); const insproxy = proxy({ target:"xxxx", prefix:"/xxx" }); app.use(insproxy.routes(), insproxy.allowedMethods());