Proxy con express.js

Para evitar problemas de AJAX del mismo dominio, quiero que mi servidor web node.js reenvíe todas las solicitudes desde la URL /api/BLABLA a otro servidor, por ejemplo other_domain.com:3000/BLABLA, y devolver al usuario lo mismo que devolvió este servidor remoto, de forma transparente.

Todas las demás URL (además de /api/*) deben ser notificados directamente, sin representación.

¿Cómo logro esto con node.js + express.js? ¿Puede dar un ejemplo de código simple?

(tanto el servidor web como el remoto 3000 el servidor está bajo mi control, ambos ejecutan node.js con express.js)


Hasta ahora encontre esto https://github.com/http-party/node-http-proxy , pero leer la documentación allí no me hizo más sabio. terminé 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
    });
});

pero no se devuelve nada al servidor web original (o al usuario final), así que no hubo suerte.

preguntado el 03 de mayo de 12 a las 17:05

la forma en que lo haces me funciona, sin ninguna modificación.

Aunque era un poco tarde para responder, estaba enfrentando un problema similar y lo resolvió eliminando el analizador del cuerpo para que el cuerpo de la solicitud no se analice antes de enviarlo por proxy. -

10 Respuestas

solicita ha quedado obsoleto a partir de febrero de 2020, dejaré la respuesta a continuación por razones históricas, pero considere pasar a una alternativa que se detalla en este , el asunto de.

Archivos

Hice algo similar pero usé solicita en lugar:

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

Espero que esto ayude, me tomó un tiempo darme cuenta de que podía hacer esto :)

contestado el 06 de mayo de 20 a las 16:05

Gracias, mucho más simple que usar la solicitud HTTP de Node.js: Alex Turpin

Aún más simple, si también canaliza la solicitud: stackoverflow.com/questions/7559862/… - Stephan Hoyer

Solución agradable y limpia. Publiqué una respuesta para que funcione también con la solicitud POST (de lo contrario, no reenvía el cuerpo de su publicación a la API). Si editas tu respuesta, me complacería eliminar la mía. - Henrik Peinar

Ver también esta respuesta para mejorar el manejo de errores. - Tamlyn

@Jonathan @trigoman ahora que request ha quedado en desuso (consulte el aviso en github.com/solicitud/solicitud), ¿cuál es la alternativa? - cervecero

Encontré una solución más corta y muy sencilla que funciona a la perfección, y también con autenticación, 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
});

Y luego simplemente:

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

Nota: como lo menciona @MaxPRafferty, use req.originalUrl en lugar de baseUrl para preservar la cadena de consulta:

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

Actualización: como lo mencionó Andrew (¡gracias!), Hay una solución preparada que utiliza el mismo principio:

npm i --save http-proxy-middleware

Y entonces:

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

Documentación: http-proxy-middleware en Github

Sé que llego tarde para unirme a esta fiesta, pero espero que esto ayude a alguien.

Respondido el 23 de junio de 20 a las 08:06

El req.url no tiene la URL completa, así que actualicé la respuesta para usar req.baseUrl en lugar de req.url - Vinoth Kumar

También me gusta usar req.originalUrl en lugar de baseUrl para conservar las cadenas de consulta, pero es posible que este no sea siempre el comportamiento deseado. - MaxPRafferty

@MaxPRafferty - comentario válido. Vale la pena señalar. Gracias. - Egoístas

Esta es la mejor solución. Estoy usando http-proxy-middleware, pero es el mismo concepto. No haga girar su propia solución de proxy cuando ya hay excelentes. - Andrés

hay otro paquete que es más simple de usar npm install express-proxy-server --save var proxy = require('express-proxy-server'); aplicación.use('/proxy', proxy('ejemplo.org/api')); - mustafa hussain

Quieres usar http.request para crear una solicitud similar a la API remota y devolver su respuesta.

Algo como esto:

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

Aviso: Realmente no he probado lo anterior, por lo que podría contener errores de análisis, con suerte esto le dará una pista sobre cómo hacer que funcione.

Respondido el 17 de diciembre de 19 a las 15:12

Sí, algunas modificaciones fueron necesarias, pero me gusta más esto que introducir una nueva dependencia adicional del módulo "Proxy". Un poco detallado, pero al menos sé exactamente lo que está pasando. Salud. - user124114

Parece que necesita hacer res.writeHead antes de que se escriba la falla de datos; de lo contrario, obtendrá un error (los encabezados no se pueden escribir después del cuerpo). - setec

@ user124114 - ponga la solución completa que ha usado - Michal Tsadok

parece que tendrá problemas para configurar los encabezados de esta manera. Cannot render headers after they are sent to the client - Shnd

Actualicé la respuesta a la sintaxis es6 y solucioné el problema de writeHead: mekwall

Extender trígonoLa respuesta de (créditos completos para él) para trabajar con POST (también podría funcionar con 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);
});

contestado el 23 de mayo de 17 a las 12:05

No pude hacerlo funcionar con PUT. Pero funciona muy bien para GET y POST. ¡¡Gracias!! - mariano desanze

@Protron para solicitudes PUT solo usa algo como if(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); } - davnicwil

Si necesita pasar encabezados como parte de una solicitud PUT o POST, asegúrese de eliminar el encabezado de longitud de contenido para que la solicitud pueda calcularlo. De lo contrario, el servidor receptor podría truncar los datos, lo que provocaría un error. - Carlos Rymer

@Henrik Peinar, ¿me ayudará esto cuando haga una solicitud de publicación de inicio de sesión y espero redirigir desde web.com/api/login a web.com/? valik

Usé la siguiente configuración para dirigir todo en /rest a mi servidor backend (en el puerto 8080) y todas las demás solicitudes al servidor frontend (un servidor webpack en el puerto 3001). Admite todos los métodos HTTP, no pierde ninguna metainformación de solicitud y admite websockets (que necesito para la recarga en caliente)

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

contestado el 07 de mayo de 16 a las 10:05

Este es el único que también se ocupa de los sockets web. - seg0ndHand

Primero instale express y http-proxy-middleware

npm install express http-proxy-middleware --save

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

En este ejemplo, atendemos el sitio en el puerto 3000, pero cuando una solicitud finaliza con /api, la redirigimos a localhost:8080.

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

Respondido el 17 de enero de 17 a las 16:01

Ok, aquí hay una respuesta lista para copiar y pegar usando el módulo npm require('request') y una variable de entorno *en lugar de un proxy codificado):

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

respondido 25 mar '16, 11:03

En lugar de una declaración de caso, todas las cuales hacen lo mismo excepto que usan una función de solicitud diferente), primero puede desinfectar (por ejemplo, una declaración if que llame a su valor predeterminado si el método no está en la lista de métodos aprobados), y luego simplemente hacer r = solicitud[método](/* el resto */); - Paul

Encontré una solución más corta que hace exactamente lo que quiero https://github.com/http-party/node-http-proxy

Después de instalar http-proxy

npm install http-proxy --save

Úselo como se muestra a continuación en su servidor/index/app.js

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

Realmente pasé días buscando en todas partes para evitar este problema, probé muchas soluciones y ninguna funcionó excepto esta.

Espero que también ayude a alguien más :)

Respondido el 24 de enero de 20 a las 10:01

Creo que deberías usar cors npm

const app = express();
const cors = require('cors');
var corsOptions = {
    origin: 'http://localhost:3000',
    optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));

https://www.npmjs.com/package/cors

Respondido 06 Abr '21, 03:04

No tengo una muestra expresa, sino una con llano http-proxy paquete. Una versión muy simplificada del proxy que usé para mi blog.

En resumen, todos los paquetes de proxy http de nodejs funcionan en el nivel del protocolo http, no en el nivel tcp (socket). Esto también es válido para express y todos los middleware express. Ninguno de ellos puede hacer proxy transparente, ni NAT, lo que significa mantener la IP de la fuente de tráfico entrante en el paquete enviado al servidor web back-end.

Sin embargo, el servidor web puede recoger la IP original de los encabezados reenviados por http x y agregarla al registro.

La xfwd: true in proxyOption habilitar la función de encabezado x-forward para 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();

Referencia para el encabezado X-Forwarded: https://en.wikipedia.org/wiki/X-Forwarded-For

Versión completa de mi proxy: https://github.com/J-Siu/ghost-https-nodejs-proxy

respondido 24 mar '17, 13:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.