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.
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.
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 :)
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.
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);
});
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);
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
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);
});
J'ai créé un module extrêmement simple qui fait exactement ceci: https://github.com/koppelaar/auth-proxy
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 :)
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