简体   繁体   English

使用 express.js 代理

[英]Proxy with express.js

To avoid same-domain AJAX issues, I want my node.js web server to forward all requests from URL /api/BLABLA to another server, for example other_domain.com:3000/BLABLA , and return to user the same thing that this remote server returned, transparently.为了避免同域 AJAX 问题,我希望我的 node.js Web 服务器将来自 URL /api/BLABLA所有请求/api/BLABLA到另一台服务器,例如other_domain.com:3000/BLABLA ,并返回给用户与此远程服务器相同的内容服务器返回,透明。

All other URLs (beside /api/* ) are to be served directly, no proxying.所有其他 URL(除了/api/* )将直接提供,没有代理。

How do I achieve this with node.js + express.js?我如何使用 node.js + express.js 实现这一点? Can you give a simple code example?能举个简单的代码例子吗?

(both the web server and the remote 3000 server are under my control, both running node.js with express.js) (web 服务器和远程3000服务器都在我的控制之下,都运行 node.js 和 express.js)


So far I found this https://github.com/http-party/node-http-proxy , but reading the documentation there didn't make me any wiser.到目前为止,我找到了这个https://github.com/http-party/node-http-proxy ,但阅读那里的文档并没有让我更明智。 I ended up with我结束了

var proxy = new httpProxy.RoutingProxy();
app.all("/api/*", function(req, res) {
    console.log("old request url " + req.url)
    req.url = '/' + req.url.split('/').slice(2).join('/'); // remove the '/api' part
    console.log("new request url " + req.url)
    proxy.proxyRequest(req, res, {
        host: "other_domain.com",
        port: 3000
    });
});

but nothing is returned to the original web server (or to the end user), so no luck.但是没有任何东西返回到原始 Web 服务器(或最终用户),所以没有运气。

request has been deprecated as of February 2020, I'll leave the answer below for historical reasons, but please consider moving to an alternative listed in this issue .自 2020 年 2 月起,请求已被弃用,出于历史原因,我将在下面留下答案,但请考虑转向本期中列出的替代方案。

Archive档案

I did something similar but I used request instead:我做了类似的事情,但我用request代替:

var request = require('request');
app.get('/', function(req,res) {
  //modify the url in any way you want
  var newurl = 'http://google.com/';
  request(newurl).pipe(res);
});

I hope this helps, took me a while to realize that I could do this :)我希望这会有所帮助,我花了一段时间才意识到我可以做到这一点:)

I found a shorter and very straightforward solution which works seamlessly, and with authentication as well, using express-http-proxy :我找到了一个更短且非常简单的解决方案,它可以无缝工作,并且还可以使用express-http-proxy进行身份验证:

const url = require('url');
const proxy = require('express-http-proxy');

// New hostname+path as specified by question:
const apiProxy = proxy('other_domain.com:3000/BLABLA', {
    proxyReqPathResolver: req => url.parse(req.baseUrl).path
});

And then simply:然后简单地:

app.use('/api/*', apiProxy);

Note: as mentioned by @MaxPRafferty, use req.originalUrl in place of baseUrl to preserve the querystring:注:如由@MaxPRafferty提到的,使用req.originalUrl代替baseUrl保存查询字符串:

    forwardPath: req => url.parse(req.baseUrl).path

Update: As mentioned by Andrew (thank you!), there's a ready-made solution using the same principle:更新:正如 Andrew 所提到的(谢谢!),有一个使用相同原理的现成解决方案:

npm i --save http-proxy-middleware

And then:进而:

const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', {target: 'http://www.example.org/api'});
app.use(apiProxy)

Documentation: http-proxy-middleware on Github文档: Github 上的 http-proxy-middleware

I know I'm late to join this party, but I hope this helps someone.我知道我加入这个聚会迟到了,但我希望这对某人有所帮助。

You want to use http.request to create a similar request to the remote API and return its response.您想使用http.request创建对远程 API 的类似请求并返回其响应。

Something like this:像这样的东西:

const http = require('http');
// or use import http from 'http';


/* your app config here */

app.post('/api/BLABLA', (oreq, ores) => {
  const options = {
    // host to forward to
    host: 'www.google.com',
    // port to forward to
    port: 80,
    // path to forward to
    path: '/api/BLABLA',
    // request method
    method: 'POST',
    // headers to send
    headers: oreq.headers,
  };

  const creq = http
    .request(options, pres => {
      // set encoding
      pres.setEncoding('utf8');

      // set http status code based on proxied response
      ores.writeHead(pres.statusCode);

      // wait for data
      pres.on('data', chunk => {
        ores.write(chunk);
      });

      pres.on('close', () => {
        // closed, let's end client request as well
        ores.end();
      });

      pres.on('end', () => {
        // finished, let's finish client request as well
        ores.end();
      });
    })
    .on('error', e => {
      // we got an error
      console.log(e.message);
      try {
        // attempt to set error message and http status
        ores.writeHead(500);
        ores.write(e.message);
      } catch (e) {
        // ignore
      }
      ores.end();
    });

  creq.end();
});

Notice: I haven't really tried the above, so it might contain parse errors hopefully this will give you a hint as to how to get it to work.注意:我还没有真正尝试过上面的方法,所以它可能包含解析错误,希望这会给你一个关于如何让它工作的提示。

To extend trigoman 's answer (full credits to him) to work with POST (could also make work with PUT etc):扩展trigoman的答案(他的全部学分)以使用 POST (也可以使用 PUT 等):

app.use('/api', function(req, res) {
  var url = 'YOUR_API_BASE_URL'+ req.url;
  var r = null;
  if(req.method === 'POST') {
     r = request.post({uri: url, json: req.body});
  } else {
     r = request(url);
  }

  req.pipe(r).pipe(res);
});

I used the following setup to direct everything on /rest to my backend server (on port 8080), and all other requests to the frontend server (a webpack server on port 3001).我使用以下设置将/rest上的所有内容定向到我的后端服务器(端口 8080),并将所有其他请求定向到前端服务器(端口 3001 上的 webpack 服务器)。 It supports all HTTP-methods, doesn't lose any request meta-info and supports websockets (which I need for hot reloading)它支持所有 HTTP 方法,不会丢失任何请求元信息并支持 websockets(我需要热重载)

var express  = require('express');
var app      = express();
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var backend = 'http://localhost:8080',
    frontend = 'http://localhost:3001';

app.all("/rest/*", function(req, res) {
  apiProxy.web(req, res, {target: backend});
});

app.all("/*", function(req, res) {
    apiProxy.web(req, res, {target: frontend});
});

var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) {
  apiProxy.ws(req, socket, head, {target: frontend});
});
server.listen(3000);

First install express and http-proxy-middleware首先安装 express 和 http-proxy-middleware

npm install express http-proxy-middleware --save

Then in your server.js然后在你的 server.js

const express = require('express');
const proxy = require('http-proxy-middleware');

const app = express();
app.use(express.static('client'));

// Add middleware for http proxying 
const apiProxy = proxy('/api', { target: 'http://localhost:8080' });
app.use('/api', apiProxy);

// Render your site
const renderIndex = (req, res) => {
  res.sendFile(path.resolve(__dirname, 'client/index.html'));
}
app.get('/*', renderIndex);

app.listen(3000, () => {
  console.log('Listening on: http://localhost:3000');
});

In this example we serve the site on port 3000, but when a request end with /api we redirect it to localhost:8080.在这个例子中,我们在端口 3000 上为站点提供服务,但是当请求以 /api 结束时,我们将其重定向到 localhost:8080。

http://localhost:3000/api/login redirect to http://localhost:8080/api/login http://localhost:3000/api/login重定向到http://localhost:8080/api/login

Ok, here's a ready-to-copy-paste answer using the require('request') npm module and an environment variable *instead of an hardcoded proxy):好的,这是使用 require('request') npm 模块和环境变量 * 而不是硬编码代理的准备复制粘贴答案:

coffeescript咖啡脚本

app.use (req, res, next) ->                                                 
  r = false
  method = req.method.toLowerCase().replace(/delete/, 'del')
  switch method
    when 'get', 'post', 'del', 'put'
      r = request[method](
        uri: process.env.PROXY_URL + req.url
        json: req.body)
    else
      return res.send('invalid method')
  req.pipe(r).pipe res

javascript: javascript:

app.use(function(req, res, next) {
  var method, r;
  method = req.method.toLowerCase().replace(/delete/,"del");
  switch (method) {
    case "get":
    case "post":
    case "del":
    case "put":
      r = request[method]({
        uri: process.env.PROXY_URL + req.url,
        json: req.body
      });
      break;
    default:
      return res.send("invalid method");
  }
  return req.pipe(r).pipe(res);
});

I found a shorter solution that does exactly what I want https://github.com/http-party/node-http-proxy我找到了一个更短的解决方案,它完全符合我的要求https://github.com/http-party/node-http-proxy

After installing http-proxy安装http-proxy

npm install http-proxy --save

Use it like below in your server/index/app.js在你的 server/index/app.js 中像下面一样使用它

var proxyServer = require('http-route-proxy');
app.use('/api/BLABLA/', proxyServer.connect({
  to: 'other_domain.com:3000/BLABLA',
  https: true,
  route: ['/']
}));

I really have spent days looking everywhere to avoid this issue, tried plenty of solutions and none of them worked but this one.我真的花了几天时间到处寻找以避免这个问题,尝试了很多解决方案,但没有一个有效,只有这个。

Hope it is going to help someone else too :)希望它也能帮助别人:)

I think you should use cors npm我认为你应该使用cors npm

const app = express();
const cors = require('cors');
var corsOptions = {
    origin: 'http://localhost:3000',
    optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));

https://www.npmjs.com/package/cors https://www.npmjs.com/package/cors

I don't have have an express sample, but one with plain http-proxy package.我没有快递样本,但有一个带有普通http-proxy包的样本。 A very strip down version of the proxy I used for my blog.我在博客中使用的代理的精简版。

In short, all nodejs http proxy packages work at the http protocol level, not tcp(socket) level.简而言之,所有 nodejs http 代理包都在 http 协议级别工作,而不是 tcp(socket) 级别。 This is also true for express and all express middleware.这也适用于 express 和所有 express 中间件。 None of them can do transparent proxy, nor NAT, which means keeping incoming traffic source IP in the packet sent to backend web server.它们都不能做透明代理,也不能做 NAT,这意味着将传入流量源 IP 保留在发送到后端 Web 服务器的数据包中。

However, web server can pickup original IP from http x-forwarded headers and add it into the log.但是,Web 服务器可以从 http x 转发的标头中获取原始 IP 并将其添加到日志中。

The xfwd: true in proxyOption enable x-forward header feature for http-proxy .xfwd: trueproxyOption启用X转发头功能http-proxy

const url = require('url');
const proxy = require('http-proxy');

proxyConfig = {
    httpPort: 8888,
    proxyOptions: {
        target: {
            host: 'example.com',
            port: 80
        },
        xfwd: true // <--- This is what you are looking for.
    }
};

function startProxy() {

    proxy
        .createServer(proxyConfig.proxyOptions)
        .listen(proxyConfig.httpPort, '0.0.0.0');

}

startProxy();

Reference for X-Forwarded Header: https://en.wikipedia.org/wiki/X-Forwarded-For X-Forwarded 标头参考: https : //en.wikipedia.org/wiki/X-Forwarded-For

Full version of my proxy: https://github.com/J-Siu/ghost-https-nodejs-proxy我的代理的完整版本: https : //github.com/J-Siu/ghost-https-nodejs-proxy

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM