Express.js içeren proxy


168

Aynı etki alanı AJAX sorunlarını önlemek için, node.js web sunucumun URL'den gelen tüm istekleri /api/BLABLAbaşka bir sunucuya iletmesini other_domain.com:3000/BLABLAve bu uzak sunucunun saydam olarak döndürdüğü şeyi kullanıcıya geri döndürmesini istiyorum.

Tüm diğer URL'ler (yanında /api/*) doğrudan sunulmalıdır, proxy yapılmaz.

Bunu node.js + express.js ile nasıl başarabilirim? Basit bir kod örneği verebilir misiniz?

(hem web sunucusu hem de uzak 3000sunucu kontrolüm altında, ikisi de express.js ile node.js çalıştırıyor)


Şimdiye kadar bu https://github.com/http-party/node-http-proxy'yi buldum , ancak belgeleri okumak beni daha akıllı hale getirmedi. İle bitirdim

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

ancak orijinal web sunucusuna (veya son kullanıcıya) hiçbir şey döndürülmez, bu yüzden şans olmaz.


yaptığınız gibi benim için çalışıyor, hiçbir değişiklik yapmadan
Saule

1
Biraz cevap vermek için çok geç olmasına rağmen, benzer bir sorunla karşı karşıya kaldı ve istek ayrıştırılmadan önce istek gövdesinin ayrıştırılmaması için gövde ayrıştırıcısını kaldırarak çözüldü.
VyvIT

Yanıtlar:


52

http.requestUzak API'ya benzer bir istek oluşturmak ve yanıtını döndürmek için kullanmak istiyorsunuz .

Bunun gibi bir şey:

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

Uyarı: Yukarıda gerçekten denemedim, bu yüzden ayrıştırma hataları içerebilir umarım bu size nasıl işe yarayacağına dair bir ipucu verecektir.


5
Evet, bazı değişiklikler gerekliydi, ama bunu yeni bir "Proxy" modül bağımlılığı getirmekten daha çok seviyorum. Biraz ayrıntılı, ama en azından neler olduğunu tam olarak biliyorum. Şerefe.
user124114

Veri chink yazılmadan önce res.writeHead yapmanız gerekiyor gibi görünüyor, aksi takdirde hata alırsınız (üstbilgiler gövdeden sonra yazılamaz).
setec

3
@ user124114 - Lütfen kullandığınız tam çözümü koyun
Michal Tsadok

1
başlıkların bu şekilde ayarlanmasında sorun yaşayacağınız anlaşılıyor. Cannot render headers after they are sent to the client
Shnd

1
Cevabını es6 sözdizimine güncelledim ve writeHead sorununu
çözdüm

219

istek Şubat 2020 itibariyle kullanımdan kaldırılmıştır, tarihsel nedenlerle aşağıdaki yanıtı bırakacağım, ancak lütfen bu sayıda listelenen bir alternatife geçmeyi düşünün .

Arşiv

Benzer bir şey yaptım ama bunun yerine istek kullandım:

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

Umarım bu yardımcı olur, bunu yapabileceğimi fark etmem biraz zaman aldı :)


5
Teşekkürler, Node.js HTTP isteğini kullanmaktan çok daha basit
Alex Turpin

17
Daha da basit, isteği de yönlendirirseniz: stackoverflow.com/questions/7559862/…
Stephan Hoyer

1
Güzel ve temiz bir çözüm. Ben de POST isteği ile çalışmak için bir cevap yayınladı (aksi takdirde posta vücudunuzu API iletmez). Cevabınızı düzenlerseniz benimkileri çıkarırsanız çok mutlu olurum.
Henrik Peinar

Ayrıca gelişmiş hata işleme için bu cevaba bakınız .
Tamlyn

Ne zaman benzer yönlendirme (ya da tam olarak) yapmaya çalıştığınızda ben aşağıdakilerle sonuçlanır: stream.js: 94 throw er; // Boruda işlenmemiş akış hatası. ^ Hata: getaddrinfo ENOTFOUND google.com errnoException (dns.js: 44: 10) at GetAddrInfoReqWrap.onlookup [ascomplete] (dns.js: 94: 26) ile ilgili herhangi bir fikriniz var mı?
keinabel

82

Sorunsuz ve kimlik doğrulamasıyla çalışan daha kısa ve çok basit bir çözüm buldum 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
});

Ve sonra basitçe:

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

Not: @MaxPRafferty tarafından belirtildiği gibi, kullanım req.originalUrlyerine baseUrlsorgu dizesi korumak için:

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

Güncelleme: Andrew tarafından belirtildiği gibi (teşekkür ederim!), Aynı prensibi kullanarak hazır bir çözüm var:

npm i --save http-proxy-middleware

Ve sonra:

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

Belgeler: Github'da http-proxy-middleware

Bu partiye katılmak için geç kaldığımı biliyorum, ama umarım bu birine yardımcı olur.


3
Req.url tam url'ye sahip değil, bu nedenle yanıtı req.url yerine req.baseUrl kullanacak şekilde güncelledi
Vinoth Kumar

1
Ben de sorgu dizeleri korumak için baseUrl yerine req.originalUrl kullanmak istiyorum, ancak bu her zaman istenen davranış olmayabilir.
MaxPRafferty

@MaxPRafferty - boş yorum. Kayda değer. Teşekkürler.
Bencil

4
Bu en iyi çözümdür. Http-proxy-middleware kullanıyorum, ama aynı kavram. Zaten harika olanları olduğunda kendi proxy çözümünüzü döndürmeyin.
Andrew

1
@tannerburton teşekkürler! Cevabı güncelledim.
Bencil

46

Uzatmak için trigoman (ayrıca PUT vb ile çalışmalarını yapabilir) POST ile işe 'ın cevabı (kendisine tam kredi):

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 ile çalıştıramadı. Ancak GET ve POST için harika çalışıyor. Teşekkür ederim!!
Mariano Desanze

5
@Protron for PUT istekleri gibi bir şey kullanınif(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
davnicwil

Bir PUT veya POST isteğinin bir parçası olarak başlıklardan geçmeniz gerekiyorsa, isteğin bunu hesaplayabilmesi için içerik uzunluğu başlığını sildiğinizden emin olun. Aksi takdirde, alıcı sunucu verileri kısaltabilir ve bu da hataya yol açabilir.
Carlos Rymer

@Henrik Peinar, bir giriş posta isteği yaptığımda ve web.com/api/login adresinden web.com/ adresine yönlendirmeyi beklediğimde bu yardımcı olacak
valik

22

Her şeyi /restarka uç sunucuma (bağlantı noktası 8080'de) ve diğer tüm istekleri ön uç sunucusuna (bağlantı noktası 3001'de bir web paketi sunucusu) yönlendirmek için aşağıdaki kurulumu kullandım . Tüm HTTP yöntemlerini destekler, herhangi bir istek meta bilgisini kaybetmez ve websockets'i destekler (sıcak yeniden yükleme için ihtiyacım olan)

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 soketleri ile ilgilenen tek kişi budur.
19:26

11

İlk önce express ve http-proxy-middleware yükleyin

npm install express http-proxy-middleware --save

Sonra server.js dosyasında

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

Bu örnekte, siteye 3000 numaralı bağlantı noktasında hizmet veriyoruz, ancak bir istek / api ile sona erdiğinde bunu localhost: 8080'e yönlendiriyoruz.

http: // localhost: 3000 / API / giriş yönlendirme için http: // localhost: 8080 / API / giriş


6

Tamam, burada zorunlu ('istek') npm modülünü ve sabit kodlu bir proxy yerine bir ortam değişkenini * kullanarak kopyalamaya hazır bir cevap var:

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:

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
Farklı bir istek işlevi kullanmak dışında hepsi aynı şeyi yapan bir vaka ifadesinden ziyade önce sterilize edebilirsiniz (örneğin, yöntem onaylanan yöntemler listesinde değilse varsayılanınızı çağıran bir if ifadesi) ve sonra r = istek [yöntem] (/ * geri kalanı * /);
Paul

2

Tam olarak istediğimi yapan daha kısa bir çözüm buldum https://github.com/http-party/node-http-proxy

Yükledikten sonra http-proxy

npm install http-proxy --save

Sunucunuzda / index / app.js dosyasında aşağıdaki gibi kullanın

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

Gerçekten bu sorunu önlemek için her yerde seyir gün geçirdim, birçok çözüm denedim ve hiçbiri çalıştı ama bu bir.

Umarım başka birine de yardım eder :)


0

Ekspres bir örneğim yok, ancak düz http-proxypakete sahip bir tane var . Blogum için kullandığım proxy'nin çok şeritli bir sürümü.

Kısacası, tüm nodejs http proxy paketleri, tcp (soket) düzeyinde değil, http protokolü düzeyinde çalışır. Bu, ekspres ve tüm ekspres ara katman yazılımları için de geçerlidir. Hiçbiri saydam proxy veya NAT yapamaz, bu da gelen trafik kaynağı IP'sini arka uç web sunucusuna gönderilen pakette tutmak anlamına gelir.

Ancak, web sunucusu orijinal IP'yi http x iletilen üstbilgilerden alabilir ve günlüğe ekleyebilir.

xfwd: trueİçerisinde proxyOptionx ileri başlık özelliği etkinleştirmek 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();

X Yönlendirilmiş Üstbilgi için Referans: https://en.wikipedia.org/wiki/X-Forwarded-For

Proxy'imin tam sürümü: https://github.com/J-Siu/ghost-https-nodejs-proxy

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.