221 votes

Proxy avec express.js

Pour éviter les problèmes de AJAX avec le même domaine, je veux que mon serveur web node.js redirige toutes les demandes de l'URL /api/BLABLA vers un autre serveur, par exemple other_domain.com:3000/BLABLA, et renvoie à l'utilisateur la même chose que ce que ce serveur distant a renvoyé, de manière transparente.

Toutes les autres URLs (à l'exception de /api/*) doivent être servies directement, sans proxy.

Comment puis-je réaliser cela avec node.js + express.js ? Pouvez-vous donner un exemple de code simplifié ?

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


Jusqu'à présent, j'ai trouvé ceci https://github.com/http-party/node-http-proxy , mais la lecture de la documentation là-bas ne m'a pas rendu plus sage. 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 renvoyé au serveur web d'origine (ou à l'utilisateur final), donc pas de chance.

0 votes

La manière dont tu le fais fonctionne pour moi, sans aucune modification

1 votes

Bien que un peu trop tard pour répondre, mais j'ai rencontré le même problème et je l'ai résolu en supprimant le corps du parseur afin que le corps de la requête ne soit pas analysé avant d'être envoyé plus loin.

242voto

trigoman Points 769

Notez que request a été obsolète en février 2020, je laisse donc la réponse ci-dessous pour des raisons historiques, mais veuillez envisager de passer à une alternative répertoriée dans ce problème, passer à l'API Fetch intégrée nativement (à partir de Node 18), ou utiliser express-http-proxy.

Réponse originale

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

var request = require('request');
app.get('/', function(req,res) {
  //modifier l'url de la manière que vous voulez
  var newurl = 'http://google.com/';
  request(newurl).pipe(res);
});

6 votes

Merci, beaucoup plus simple que d'utiliser la requête HTTP de Node.js

18 votes

Encore plus simple, si vous redirigez également la demande: stackoverflow.com/questions/7559862/…

1 votes

Belle et propre solution. J'ai publié une réponse pour le faire fonctionner également avec une requête POST (sinon, il ne transmet pas le corps de votre message à l'API). Si vous modifiez votre réponse, je serais heureux de supprimer la mienne.

139voto

Nitai J. Perez Points 1

J'ai trouvé une solution plus courte et très directe qui fonctionne parfaitement, et avec l'authentification aussi, en utilisant express-http-proxy:

const url = require('url');
const proxy = require('express-http-proxy');

// Nouveau nom d'hôte+chemin tel que spécifié dans la question:
const apiProxy = proxy('other_domain.com:3000/BLABLA', {
    proxyReqPathResolver: req => url.parse(req.baseUrl).path
});

Et ensuite simplement :

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

Remarque : comme indiqué par @MaxPRafferty, utilisez req.originalUrl à la place de baseUrl pour préserver 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 prête à l'emploi utilisant le même principe :

npm i --save http-proxy-middleware

Et ensuite :

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

5 votes

Le req.url n'a pas l'URL complète, donc j'ai mis à jour la réponse pour utiliser req.baseUrl au lieu de req.url

4 votes

Je préfère également utiliser req.originalUrl à la place de baseUrl pour conserver les chaînes de requête, mais cela peut ne pas toujours être le comportement souhaité.

0 votes

@MaxPRafferty - commentaire valide. Mérite d'être noté. Merci.

69voto

Marcus Ekwall Points 14489

Vous voulez utiliser http.request pour créer une requête similaire à l'API distante et renvoyer sa réponse.

Quelque chose comme ceci :

const http = require('http');
// ou utilisez import http from 'http';

/* votre configuration d'application ici */

app.post('/api/BLABLA', (oreq, ores) => {
  const options = {
    // hôte de renvoi
    host: 'www.google.com',
    // port de renvoi
    port: 80,
    // chemin de renvoi
    path: '/api/BLABLA',
    // méthode de requête
    method: 'POST',
    // en-têtes à envoyer
    headers: oreq.headers,
  };

  const creq = http
    .request(options, pres => {
      // définir l'encodage
      pres.setEncoding('utf8');

      // définir le code d'état HTTP en fonction de la réponse renvoyée
      ores.writeHead(pres.statusCode);

      // attendre les données
      pres.on('data', chunk => {
        ores.write(chunk);
      });

      pres.on('close', () => {
        // fermé, terminons également la demande du client
        ores.end();
      });

      pres.on('end', () => {
        // terminé, terminons également la demande du client
        ores.end();
      });
    })
    .on('error', e => {
      // nous avons eu une erreur
      console.log(e.message);
      try {
        // tentative de définir le message d'erreur et le code d'état HTTP
        ores.writeHead(500);
        ores.write(e.message);
      } catch (e) {
        // ignorer
      }
      ores.end();
    });

  creq.end();
});

Remarque : Je n'ai pas vraiment essayé ce qui précède, donc cela pourrait contenir des erreurs d'analyse ; espérons que cela vous donnera une indication sur la façon de le faire fonctionner.

12 votes

Oui, quelques modifications étaient nécessaires, mais je préfère cela plutôt que d'introduire une nouvelle dépendance de module "Proxy" supplémentaire. Un peu verbeux, mais au moins je sais exactement ce qui se passe. Santé.

0 votes

Il semble que vous devez faire res.writeHead avant d'écrire des data chunk, sinon vous obtiendrez une erreur (les en-têtes ne peuvent pas être écrits après le corps).

5 votes

@user124114 - veuillez mettre la solution complète que vous avez utilisée

53voto

Henrik Peinar Points 1020

Pour étendre la réponse de trigoman (tous les crédits lui reviennent) pour fonctionner avec POST (peut également fonctionner avec PUT etc) :

app.use('/api', function(req, res) {
  var url = 'VOTRE_URL_DE_BASE_API'+ 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);
});

2 votes

Je n'ai pas pu le faire fonctionner avec PUT. Mais ça marche très bien pour GET et POST. Merci!!

6 votes

@Protron pour les requêtes PUT, il suffit d'utiliser quelque chose comme if(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }

0 votes

Si vous devez transmettre des en-têtes dans le cadre d'une requête PUT ou POST, assurez-vous de supprimer l'en-tête de contenu longueur pour que la requête puisse le calculer. Sinon, le serveur récepteur pourrait tronquer les données, ce qui entraînerait une erreur.

29voto

Anthony De Smet Points 712

J'ai utilisé la configuration suivante pour rediriger tout sur /rest vers mon serveur backend (sur le port 8080), et toutes les autres requêtes vers le serveur frontend (un serveur webpack sur le port 3001). Il prend en charge toutes les méthodes HTTP, ne perd aucune information méta-requête et prend en charge 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);

4 votes

Il s'agit du seul qui traite également des websockets.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X