express.jsによるプロキシ


168

同じドメインのAJAXの問題を回避するために、node.js WebサーバーがURL /api/BLABLAからのすべてのリクエストを別のサーバーに転送しother_domain.com:3000/BLABLA、このリモートサーバーが返したものと同じものを透過的にユーザーに返します。

他のすべてのURL(以外/api/*)は、プロキシなしで直接提供されます。

node.js + express.jsでこれを実現するにはどうすればよいですか?簡単なコード例を教えてください。

(Webサーバーとリモート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サーバー(またはエンドユーザー)には何も返されないため、運がありません。


あなたがそれをする方法は何の変更もなしに私のために働いています
Saule

1
少し遅すぎますが、同様の問題に直面していて、リクエストボディがさらにプロキシされる前に解析されないようにボディパーサーを削除することで解決しました。
VyvIT 2017

回答:


52

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

通知:私は実際には上記を試していませんので、解析エラーが含まれている可能性があります。うまくいけば、これがどのように機能するかについてのヒントが得られるでしょう。


5
ええ、いくつかの変更が必要でしたが、私はこれが、新しい「プロキシ」モジュールの依存関係を追加するよりも好きです。少し冗長ですが、少なくとも私は何が起こっているのか正確に知っています。乾杯。
user124114

データチャンクが書き込まれる前にres.writeHeadを実行する必要があるようです。そうしないと、エラーが発生します(ヘッダーを本文の後に書き込むことはできません)。
setec

3
@ user124114-使用した完全なソリューションを入力してください
Michal Tsadok

1
この方法でヘッダーを設定する際に問題が発生するようです。Cannot render headers after they are sent to the client
Shnd

1
es6構文の回答を更新し、writeHeadの問題を修正しました
mekwall

219

リクエストは2020年2月の時点で非推奨になりました。歴史的な理由により、以下の回答は残しておきますが、この問題に記載されている別の方法への移行を検討してください。

アーカイブ

私は同様のことをしましたが、代わりにリクエストを使用しました:

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

これが役に立てば幸いです。これができることを理解するのにしばらく時間がかかりました:)


5
おかげで、Node.jsのHTTPリクエストを使用するよりもはるかに簡単
Alex Turpin

17
さらに簡単、あなたもパイプの要求の場合: stackoverflow.com/questions/7559862/...
シュテファン・ホイヤー

1
素敵でクリーンなソリューション。POSTリクエストでも機能するように回答を投稿しました(そうしないと、投稿の本文がAPIに転送されません)。回答を編集した場合は、削除させていただきます。
Henrik Peinar 2014年

エラー処理の改善については、この回答も参照してください。
Tamlyn、2014年

同様のルーティング(またはまったく同じ)を実行しようとすると、次のようになります。stream.js:94 throw er; //パイプでの未処理のストリームエラー。^エラー:getaddrinfo ENOTFOUND google.com at errnoException(dns.js:44:10)at GetAddrInfoReqWrap.onlookup [as oncomplete](dns.js:94:26)任意のアイデア?
ケイナベル

82

私はシームレスに、そして認証でも機能する、短くて非常に簡単なソリューションを見つけました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.originalUrlbaseUrlてクエリ文字列を保持します。

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

更新:Andrew(ありがとう!)が述べたように、同じ原理を使用した既製のソリューションがあります。

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)

ドキュメント:Github上のhttp-proxy-middleware

このパーティーに参加するのが遅いのはわかっていますが、これが誰かのお役に立てば幸いです。


3
req.urlには完全なURLがないため、req.urlではなくreq.baseUrlを使用するように回答を更新しました
Vinoth Kumar

1
また、クエリ文字列を保持するためにbaseUrlの代わりにreq.originalUrlを使用することも好きですが、これが常に望ましい動作であるとは限りません。
MaxPRafferty 2016年

@MaxPRafferty-無効なコメント。注目に値する。ありがとう。
2016年

4
これが最良の解決策です。私はhttp-proxy-middlewareを使用していますが、それは同じ概念です。すでに優れたプロキシソリューションがある場合は、独自のプロキシソリューションをスピンしないでください。
Andrew

1
@tannerburtonありがとう!答えを更新しました。
利己的な

46

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

1
PUTで動作させることができませんでした。ただし、GETおよびPOSTには適しています。ありがとうございました!!
マリアーノデサンツェ14

5
@Protron for PUTリクエストは次のようなものを使用しますif(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
davnicwil

PUTまたはPOSTリクエストの一部としてヘッダーを渡す必要がある場合は、リクエストが計算できるようにcontent-lengthヘッダーを必ず削除してください。そうしないと、受信サーバーがデータを切り捨て、エラーが発生する可能性があります。
Carlos Rymer、2015年

@Henrik Peinarは、ログインポストリクエストを実行し、web.com
api / login

22

次の設定を使用して、すべてを/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);

1
これは、Webソケットも処理する唯一のものです。
sec0ndHand

11

最初に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へのリダイレクト


6

さて、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);
});

2
異なるリクエスト関数を使用することを除いてすべて同じことを行うcaseステートメントではなく、最初にサニタイズ(たとえば、メソッドが承認済みメソッドのリストにない場合にデフォルトを呼び出すifステートメント)を行い、次にただ実行することができます。 r = request [method](/ *残り* /);
ポール

2

私が望んでいることを正確に行う短い解決策を見つけました 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: ['/']
}));

私は本当にこの問題を回避するためにあらゆる場所を探すのに何日も費やしてきましたが、たくさんの解決策を試してみましたが、どれもうまくいきませんでした。

それが他の誰かにも役立つことを願っています:)


0

明示的なサンプルはありませんが、プレーンhttp-proxyパッケージのサンプルがあります。私のブログで使用したプロキシの非常にストリップバージョン。

つまり、すべてのnodejs httpプロキシパッケージは、tcp(socket)レベルではなく、httpプロトコルレベルで機能します。これは、ExpressおよびすべてのExpressミドルウェアにも当てはまります。それらのいずれも透過プロキシもNATも実行できません。つまり、バックエンドWebサーバーに送信されるパケット内の受信トラフィックソースIPを維持します。

ただし、Webサーバーはhttp x-forwardedヘッダーから元のIPを取得してログに追加できます。

Xフォワードヘッダ機能を有効。xfwd: trueproxyOptionhttp-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();

X-Forwardedヘッダーのリファレンス:https : //en.wikipedia.org/wiki/X-Forwarded-For

私のプロキシの完全版:https : //github.com/J-Siu/ghost-https-nodejs-proxy

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.