Proxy con express.js


168

Per evitare problemi con lo stesso dominio AJAX, desidero che il mio server web node.js inoltri tutte le richieste dall'URL /api/BLABLAa un altro server, ad esempio other_domain.com:3000/BLABLA, e ritorni all'utente la stessa cosa che questo server remoto ha restituito, in modo trasparente.

Tutti gli altri URL (accanto /api/*) devono essere offerti direttamente, senza proxy.

Come posso ottenere questo risultato con node.js + express.js? Puoi fare un semplice esempio di codice?

(sia il server Web che il 3000server remoto sono sotto il mio controllo, entrambi eseguono node.js con express.js)


Finora ho trovato questo https://github.com/http-party/node-http-proxy , ma leggere la documentazione non mi ha reso più saggio. Ho finito con

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

ma nulla viene restituito al server Web originale (o all'utente finale), quindi nessuna fortuna.


il modo in cui lo fai funziona per me, senza alcuna modifica
Saule

1
Anche se un po 'troppo tardi per rispondere, ma stava affrontando un problema simile e lo ha risolto rimuovendo il parser del corpo in modo che il corpo della richiesta non venga analizzato prima di inviarlo ulteriormente.
VyvIT,

Risposte:


52

Si desidera utilizzare http.requestper creare una richiesta simile all'API remota e restituire la sua risposta.

Qualcosa come questo:

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

Avviso: non ho davvero provato quanto sopra, quindi potrebbe contenere errori di analisi, spero che questo ti darà un suggerimento su come farlo funzionare.


5
Sì, alcune modifiche erano necessarie, ma mi piace di più che introdurre una nuova dipendenza del modulo "Proxy". Un po 'prolisso, ma almeno so esattamente cosa sta succedendo. Saluti.
user124114,

Sembra che tu debba fare res.writeHead prima che venga scritto il chink dei dati, altrimenti otterrai un errore (le intestazioni non possono essere scritte dopo il corpo).
setec,

3
@ user124114 - inserisci la soluzione completa che hai utilizzato
Michal Tsadok,

1
sembra che avrai problemi a impostare le intestazioni in questo modo. Cannot render headers after they are sent to the client
Shnd

1
Ho aggiornato la risposta alla sintassi es6 e risolto il problema
writeHead

219

la richiesta è stata ritirata dal febbraio 2020, lascerò la risposta di seguito per motivi storici, ma ti preghiamo di considerare di passare a un'alternativa elencata in questo numero .

Archivio

Ho fatto qualcosa di simile ma ho usato invece la richiesta :

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

Spero che questo aiuti, mi ci è voluto un po 'per capire che avrei potuto farlo :)


5
Grazie, molto più semplice rispetto all'utilizzo della richiesta HTTP di Node.js
Alex Turpin,

17
Ancora più semplice, se convogli anche la richiesta: stackoverflow.com/questions/7559862/…
Stephan Hoyer,

1
Soluzione piacevole e pulita. Ho pubblicato una risposta per farlo funzionare anche con la richiesta POST (altrimenti non inoltra il tuo corpo post all'API). Se modifichi la tua risposta sarei felice di rimuovere la mia.
Henrik Peinar,

Vedi anche questa risposta per una migliore gestione degli errori.
Tamlyn,

ogni volta che provo a fare un routing simile (o esattamente lo stesso) finisco con il seguente: stream.js: 94 throw er; // Errore flusso non gestito nella pipe. ^ Errore: getaddrinfo ENOTFOUND google.com su errnoException (dns.js: 44: 10) su GetAddrInfoReqWrap.onlookup [come incompleto] (dns.js: 94: 26) qualche idea?
keinabel,

82

Ho trovato una soluzione più breve e molto semplice che funziona senza problemi e anche con l'autenticazione, usando 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
});

E poi semplicemente:

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

Nota: come indicato da @MaxPRafferty, utilizzare req.originalUrlal posto di baseUrlper preservare la stringa di querys :

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

Aggiornamento: come menzionato da Andrew (grazie!), Esiste una soluzione pronta che utilizza lo stesso principio:

npm i --save http-proxy-middleware

E poi:

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

Documentazione: http-proxy-middleware su Github

So che sono in ritardo per unirmi a questa festa, ma spero che questo aiuti qualcuno.


3
Req.url non ha l'URL completo, quindi ho aggiornato la risposta per utilizzare req.baseUrl invece di req.url
Vinoth Kumar il

1
Mi piace anche usare req.originalUrl al posto di baseUrl per preservare le stringhe di query, ma questo potrebbe non essere sempre il comportamento desiderato.
MaxPRafferty

@MaxPRafferty - vaid comment. Vale la pena notare. Grazie.
Egoista

4
Questa è la soluzione migliore Sto usando http-proxy-middleware, ma è lo stesso concetto. Non girare la tua soluzione proxy quando ce ne sono già di fantastici là fuori.
Andrew,

1
@tannerburton grazie! Ho aggiornato la risposta.
Egoista il

46

Per estendere la risposta del trigoman (crediti a lui pieni) al lavoro con POST (potrebbe anche funzionare con PUT ecc.)

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
Impossibile farlo funzionare con PUT. Ma funziona benissimo per GET e POST. Grazie!!
Mariano Desanze,

5
@Protron per richieste PUT basta usare qualcosa del genereif(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
davnicwil,

Se è necessario passare attraverso le intestazioni come parte di una richiesta PUT o POST, assicurarsi di eliminare l'intestazione della lunghezza del contenuto in modo che la richiesta possa calcolarla. Altrimenti, il server ricevente potrebbe troncare i dati, il che provocherà un errore.
Carlos Rymer,

@Henrik Peinar, sarà di aiuto quando faccio una richiesta di post di accesso e mi aspetto di reindirizzare da web.com/api/login a web.com/
valik

22

Ho usato la seguente configurazione per indirizzare tutto sul /restmio server back-end (sulla porta 8080) e tutte le altre richieste al server front-end (un server webpack sulla porta 3001). Supporta tutti i metodi HTTP, non perde alcuna meta-informazione richiesta e supporta i websocket (di cui ho bisogno per ricaricare a caldo)

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
Questo è l'unico che si occupa anche dei socket web.
sec0ndHand

11

Prima installa express e http-proxy-middleware

npm install express http-proxy-middleware --save

Quindi nel tuo 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 questo esempio serviamo il sito sulla porta 3000, ma quando una richiesta termina con / api lo reindirizziamo a localhost: 8080.

http: // localhost: 3000 / api / login reindirizzare a http: // localhost: 8080 / api / login


6

Ok, ecco una risposta pronta per essere incollata usando il modulo npm request ('request') e una variabile d'ambiente * invece di un proxy hardcoded):

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
Piuttosto che un'istruzione case che fa tutte la stessa cosa tranne che utilizzare una funzione di richiesta diversa) è possibile disinfettare prima (ad esempio un'istruzione if che chiama l'impostazione predefinita se il metodo non è nell'elenco dei metodi approvati), quindi r = request [metodo] (/ * il resto * /);
Paul,

2

Ho trovato una soluzione più breve che fa esattamente quello che voglio https://github.com/http-party/node-http-proxy

Dopo l'installazione http-proxy

npm install http-proxy --save

Usalo come di seguito nel tuo 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: ['/']
}));

Ho davvero passato giorni a cercare ovunque per evitare questo problema, ho provato molte soluzioni e nessuna di esse ha funzionato tranne questa.

Spero che possa aiutare anche qualcun altro :)


0

Non ho un campione espresso, ma uno con un http-proxypacchetto semplice . Una versione molto ridotta del proxy che ho usato per il mio blog.

In breve, tutti i pacchetti proxy http nodejs funzionano a livello di protocollo http, non a livello di tcp (socket). Questo vale anche per il middleware express e all express. Nessuno di loro può fare proxy trasparente, né NAT, il che significa mantenere l'IP di origine del traffico in entrata nel pacchetto inviato al server Web back-end.

Tuttavia, il server Web può prelevare l'IP originale dalle intestazioni http x-forwarded e aggiungerlo nel registro.

La funzione xfwd: truedi proxyOptionattivazione dell'intestazione x-forward in per 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();

Riferimento per X-Forwarded Header: https://en.wikipedia.org/wiki/X-Forwarded-For

Versione completa del mio proxy: https://github.com/J-Siu/ghost-https-nodejs-proxy

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.