web-dev-qa-db-fra.com

Proxy avec express.js

Pour éviter les problèmes de même domaine AJAX, je souhaite que mon serveur Web node.js transfère toutes les demandes de l'URL /api/BLABLA à un autre serveur, par exemple other_domain.com:3000/BLABLA, et renvoie à l'utilisateur le même chose que ce serveur distant est retourné, de manière transparente.

Toutes les autres URL (à côté de /api/*) doivent être servies directement, sans proxy.

Comment y parvenir avec node.js + express.js? Pouvez-vous donner un exemple de code simple?

(le serveur Web et le serveur 3000 distant sont sous mon contrôle, ils exécutent tous deux node.js avec express.js)


Jusqu'à présent, j'ai trouvé ceci https://github.com/nodejitsu/node-http-proxy/ , mais la lecture de la documentation ne m'a pas éclairé. J'ai fini avec

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

mais rien n'est retourné au serveur Web d'origine (ou à l'utilisateur final), donc pas de chance.

141
user124114

Vous souhaitez utiliser http.request pour créer une demande similaire à l'API distante et renvoyer sa réponse.

Quelque chose comme ça:

_var http = require('http');

/* your app config here */

app.post('/api/BLABLA', function(req, res) {

  var 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: req.headers
  };

  var creq = http.request(options, function(cres) {

    // set encoding
    cres.setEncoding('utf8');

    // wait for data
    cres.on('data', function(chunk){
      res.write(chunk);
    });

    cres.on('close', function(){
      // closed, let's end client request as well 
      res.writeHead(cres.statusCode);
      res.end();
    });

    cres.on('end', function(){
      // finished, let's finish client request as well 
      res.writeHead(cres.statusCode);
      res.end();
    });

  }).on('error', function(e) {
    // we got an error, return 500 error to client and log error
    console.log(e.message);
    res.writeHead(500);
    res.end();
  });

  creq.end();

});
_

Remarque: je n'ai pas vraiment essayé ce qui précède, il pourrait donc contenir des erreurs d'analyse, espérons que cela vous donnera un indice sur la manière de le faire fonctionner.

43
mekwall

J'ai fait quelque chose de similaire mais j'ai utilisé demande à la place:

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

J'espère que cela aide, m'a pris un certain temps pour réaliser que je pouvais le faire :)

194
trigoman

J'ai trouvé une solution courte et très simple qui fonctionne parfaitement, avec authentification également, en utilisant 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', {
    forwardPath: req => url.parse(req.baseUrl).path
});

Et puis simplement:

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

Remarque: comme mentionné par @MaxPRafferty, utilisez req.originalUrl à la place de baseUrl pour conserver la chaîne de requête:

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

Mise à jour: Comme l'a mentionné Andrew (merci!), Il existe une solution toute prête utilisant le même principe:

npm i --save http-proxy-middleware

Puis:

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

Documentation: http-proxy-middleware sur Github

Je sais que je suis en retard pour rejoindre ce parti, mais j'espère que cela aidera quelqu'un.

61
Selfish

Pour étendre la réponse de trigoman (crédits complets à lui) pour travailler avec POST (pourrait également fonctionner avec PUT, etc.):

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);
});
44
Henrik Peinar

J'ai utilisé la configuration suivante pour tout diriger sur /rest vers mon serveur principal (sur le port 8080), ainsi que pour toutes les autres demandes adressées au serveur frontal (un serveur Webpack sur le port 3001). Il supporte toutes les méthodes HTTP, ne perd aucune méta-information de requête et supporte les websockets (dont j'ai besoin pour le rechargement à chaud)

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);
19
Anthony De Smet

Première installation express et http-proxy-middleware

npm install express http-proxy-middleware --save

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

Dans cet exemple, nous servons le site sur le port 3000, mais lorsqu'une demande se termine par/api, nous le redirigeons vers localhost: 8080.

http: // localhost: 3000/api/login redirection vers http: // localhost: 8080/api/login

8
C. Dupetit

Ok, voici une réponse prête à copier-coller utilisant le module npm require ('request') et une variable d'environnement * à la place d'un proxy codé en dur):

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

J'ai créé un module extrêmement simple qui fait exactement ceci: https://github.com/koppelaar/auth-proxy

2
koppelaar

J'ai trouvé une solution plus courte qui fait exactement ce que je veux https://github.com/nodejitsu/node-http-proxy/

Après avoir installé http-proxy

npm install http-proxy --save

Utilisez-le comme ci-dessous dans votre serveur/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'ai vraiment passé des jours à chercher partout pour éviter ce problème, j'ai essayé de nombreuses solutions et aucune d'entre elles ne fonctionnait si ce n'est celle-ci.

J'espère que ça va aider quelqu'un d'autre aussi :)

1
hzitoun

Je n'ai pas d'échantillon express, mais un échantillon contenant le package http-proxy simple. Une version très simple du proxy que j'ai utilisé pour mon blog.

En bref, tous les packages de proxy http nodejs fonctionnent au niveau du protocole http, pas au niveau du TCP (socket). Ceci est également vrai pour les middlewares express et express. Aucun d'entre eux ne peut utiliser de proxy transparent, ni de NAT, ce qui implique de conserver l'IP source du trafic entrant dans le paquet envoyé au serveur Web principal.

Cependant, le serveur Web peut récupérer l'IP d'origine à partir des en-têtes http x-forwarded et l'ajouter au journal.

Le xfwd: true dans proxyOption active la fonction d’en-tête de transfert x pour 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();

Référence pour l'en-tête X-Forwarded: https://en.wikipedia.org/wiki/X-Forwarded-For

Version complète de mon proxy: https://github.com/J-Siu/ghost-https-nodejs-proxy

0
John Siu