ChatGPT解决这个技术问题 Extra ChatGPT

express.js 代理

为避免同域 AJAX 问题,我希望我的 node.js Web 服务器将来自 URL /api/BLABLA 的所有请求转发到另一台服务器,例如 other_domain.com:3000/BLABLA,并以透明方式向用户返回此远程服务器返回的相同内容。

所有其他 URL(除了 /api/*)将直接提供,无需代理。

如何使用 node.js + express.js 实现这一点?你能举一个简单的代码例子吗?

(网络服务器和远程 3000 服务器都在我的控制之下,都运行 node.js 和 express.js)

到目前为止,我找到了这个 https://github.com/http-party/node-http-proxy ,但是阅读那里的文档并没有让我变得更聪明。我结束了

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
    });
});

但是没有任何东西返回到原始 Web 服务器(或最终用户),所以没有运气。

你这样做的方式对我有用,没有任何修改
虽然回答有点晚了,但遇到了类似的问题并通过删除正文解析器来解决它,以便在进一步代理之前不会解析请求正文。

t
trigoman

请求已于 2020 年 2 月弃用,由于历史原因,我将在下面留下答案,但请考虑移至本期中列出的替代方案。

档案

我做了类似的事情,但我使用 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);
});

我希望这会有所帮助,我花了一段时间才意识到我可以做到这一点:)


谢谢,比使用 Node.js 的 HTTP 请求简单得多
更简单的是,如果您还通过管道发送请求:stackoverflow.com/questions/7559862/…
漂亮干净的解决方案。我发布了一个答案以使其也适用于 POST 请求(否则它不会将您的帖子正文转发到 API)。如果您编辑答案,我很乐意删除我的答案。
@Jonathan @trigoman 现在 request 已被弃用(请参阅 github.com/request/request 处的通知),还有什么替代方案?
This 答案帮助我使用 node-fetch 提供了一个未弃用的解决方案。
S
Selfish

我找到了一个更短且非常简单的解决方案,它可以无缝运行,并且还可以使用 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
});

然后简单地说:

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

注意:正如@MaxPRafferty 所述,使用 req.originalUrl 代替 baseUrl 来保留查询字符串:

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

更新:正如安德鲁所提到的(谢谢!),有一个使用相同原理的现成解决方案:

npm i --save http-proxy-middleware

接着:

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

文档:http-proxy-middleware on Github

我知道我加入这个聚会迟到了,但我希望这对某人有所帮助。


req.url 没有完整的 url,因此更新了答案以使用 req.baseUrl 而不是 req.url
我也喜欢使用 req.originalUrl 代替 baseUrl 来保留查询字符串,但这可能并不总是理想的行为。
@MaxPRafferty - 无效评论。值得注意。谢谢。
这是最好的解决方案。我正在使用 http-proxy-middleware,但它是相同的概念。当已经有很棒的代理解决方案时,不要旋转自己的代理解决方案。
还有另一个使用起来更简单的包 npm install express-proxy-server --save var proxy = require('express-proxy-server'); app.use('/proxy', proxy('example.org/api'));
m
mekwall

您想使用 http.request 创建对远程 API 的类似请求并返回其响应。

像这样的东西:

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();
});

注意:我还没有真正尝试过上面的方法,所以它可能包含解析错误,希望这会给你一个关于如何让它工作的提示。


是的,一些修改是必要的,但我更喜欢这个,而不是引入一个额外的新“代理”模块依赖项。有点冗长,但至少我确切地知道发生了什么。干杯。
似乎您需要在写入数据缝隙之前执行 res.writeHead ,否则您会收到错误(标题不能在正文之后写入)。
@user124114 - 请输入您使用过的完整解决方案
似乎您以这种方式设置标题会遇到问题。 Cannot render headers after they are sent to the client
我已经更新了 es6 语法的答案并修复了 writeHead 问题
C
Community

扩展 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);
});

无法使其与 PUT 一起使用。但适用于 GET 和 POST。谢谢!!
@Protron 用于 PUT 请求只需使用 if(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
如果您需要将标头作为 PUT 或 POST 请求的一部分传递,请确保删除 content-length 标头,以便请求可以计算它。否则,接收服务器可能会截断数据,这将导致错误。
@Henrik Peinar,当我发出登录帖子请求并期望从 web.com/api/login 重定向到 web.com/ 时,这会有所帮助吗
A
Anthony De Smet

我使用以下设置将 /rest 上的所有内容定向到我的后端服务器(端口 8080),并将所有其他请求定向到前端服务器(端口 3001 上的 webpack 服务器)。它支持所有 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);

这是唯一一个也处理网络套接字的。
C
C. Dupetit

首先安装 express 和 http-proxy-middleware

npm install express http-proxy-middleware --save

然后在你的 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');
});

在此示例中,我们在端口 3000 上为站点提供服务,但是当请求以 /api 结尾时,我们将其重定向到 localhost:8080。

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


c
coderofsalvation

好的,这是使用 require('request') npm 模块和环境变量 * 而不是硬编码代理的准备复制粘贴答案):

咖啡脚本

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:

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);
});

除了使用不同的请求函数之外,所有的 case 语句都做同样的事情)你可以先清理(例如,如果方法不在批准的方法列表中,则调用你的默认值的 if 语句),然后就做r = request[method](/* 剩下的 */);
t
tanguy_k

我找到了一个更短的解决方案,完全符合我的要求https://github.com/http-party/node-http-proxy

安装 http-proxy

npm install http-proxy --save

在你的 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: ['/']
}));

为了避免这个问题,我真的花了好几天的时间到处寻找,尝试了很多解决方案,但除了这个之外,它们都没有奏效。

希望它也能帮助别人:)


J
John Siu

我没有快递样品,但有一个带有普通 http-proxy 包的样品。我用于博客的代理的一个非常精简的版本。

简而言之,所有 nodejs http 代理包都工作在 http 协议级别,而不是 tcp(socket) 级别。对于 express 和所有 express 中间件也是如此。它们都不能做透明代理,也不能做 NAT,这意味着将传入流量源 IP 保留在发送到后端 Web 服务器的数据包中。

但是,Web 服务器可以从 http x-forwarded 标头中获取原始 IP 并将其添加到日志中。

proxyOption 中的 xfwd: truehttp-proxy 启用 x-forward 标头功能。

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();

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

我的代理的完整版:https://github.com/J-Siu/ghost-https-nodejs-proxy


L
Loc Tran

我认为您应该使用 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