express.js를 사용한 프록시


168

동일한 도메인 AJAX 문제를 피하기 위해 node.js 웹 서버가 URL의 모든 요청 /api/BLABLA을 다른 서버 로 전달 other_domain.com:3000/BLABLA하고이 원격 서버가 투명하게 반환 한 것과 동일한 것을 사용자에게 반환하기를 원합니다 .

다른 모든 URL ( /api/*)은 프록시없이 직접 제공됩니다.

node.js + express.js로 어떻게 이것을 달성합니까? 간단한 코드 예제를 줄 수 있습니까?

(웹 서버와 원격 3000서버 모두 내 제어하에 있으며 모두 express.js로 node.js를 실행합니다)


지금 까지이 https://github.com/http-party/node-http-proxy를 찾았 지만 거기에서 설명서를 읽는 것이 더 현명하지는 않았습니다. 나는 결국

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

그러나 원래 웹 서버 (또는 최종 사용자)에게 아무것도 반환되지 않으므로 운이 없습니다.


당신이하는 방법은 아무런 수정없이 나를 위해 일하고 있습니다
Saule

1
답변하기에는 너무 늦었지만 비슷한 문제에 직면하고 있으며 본문을 파싱하기 전에 본문을 파싱하지 않도록 본문 파서를 제거하여 문제를 해결했습니다.
VyvIT

답변:


52

http.request원격 API와 유사한 요청을 작성하고 응답을 리턴하는 데 사용하려고합니다 .

이 같은:

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

주의 사항 : 나는 실제로 위의 시도를하지 않았으므로 구문 분석 오류가 포함될 수 있기를 바랍니다.이 작업을 수행하는 방법에 대한 힌트를 줄 것입니다.


5
네, 약간의 수정이 필요했지만, 새로운 "프록시"모듈 의존성을 도입하는 것보다이 방법이 더 좋습니다. 조금 장황하지만 적어도 나는 무슨 일이 일어나고 있는지 정확히 알고 있습니다. 건배.
user124114

데이터 chink가 작성되기 전에 res.writeHead를 수행 해야하는 것처럼 보입니다. 그렇지 않으면 오류가 발생합니다 (본문 뒤에 헤더를 쓸 수 없음).
setec

3
@ user124114 - 당신이 사용하고있는 전체 솔루션 넣어주십시오
마이클 Tsadok

1
이 방법으로 헤더를 설정하는 데 문제가있는 것 같습니다. Cannot render headers after they are sent to the client
Shnd

1
내가 ES6 구문에 대한 답을 업데이트하고 writeHead의 문제를 해결 한
mekwall

219

2020 년 2 월부터 요청 이 더 이상 사용되지 않습니다. 역사적인 이유로 아래 답변을 남겨 두겠습니다. 그러나이 문제에 나열된 대안으로 이동하시기 바랍니다 .

보관

비슷한 것을했지만 대신 요청을 사용했습니다.

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

나는 이것이 도움이되기를 바랍니다. 내가 이것을 할 수 있다는 것을 깨닫는 데 시간이 걸렸습니다. :)


5
감사합니다. Node.js의 HTTP 요청을 사용하는 것보다 훨씬 간단합니다
Alex Turpin

17
심지어 간단한, 당신도 파이프를 요청하는 경우 : stackoverflow.com/questions/7559862/...
스테판 호이어

1
좋고 깨끗한 솔루션. POST 요청에서도 작동하도록 답변을 게시했습니다 (그렇지 않으면 게시물 본문을 API로 전달하지 않습니다). 답변을 수정하면 기꺼이 제거하겠습니다.
Henrik Peinar

향상된 오류 처리에 대해서는 이 답변 을 참조하십시오 .
Tamlyn

비슷한 라우팅 (또는 똑같은)을 시도 할 때마다 다음과 같이 끝납니다. stream.js : 94 throw er; // 파이프에서 처리되지 않은 스트림 오류. ^ 오류 : GetAddrInfoReqWrap.onlookup의 errnoException (dns.js : 44 : 10)에서 getaddrinfo ENOTFOUND google.com (완료 됨) (dns.js : 94 : 26) 어떤 아이디어가 있습니까?
keinabel

82

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

그리고 간단히 :

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

참고 : @MaxPRafferty에서 언급했듯이 쿼리 문자열을 보존하는 req.originalUrl대신 사용 baseUrl하십시오.

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

업데이트 : Andrew (감사합니다!)가 언급했듯이 동일한 원칙을 사용하여 기성품 솔루션이 있습니다.

npm i --save http-proxy-middleware

그리고:

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

설명서 : Github의 http-proxy-middleware

나는이 파티에 늦었다는 것을 알고 있지만 이것이 누군가에게 도움이되기를 바랍니다.


3
req.url에는 전체 URL이 없으므로 req.url 대신 req.baseUrl을 사용하도록 답변을 업데이트했습니다.
Vinoth Kumar

1
또한 쿼리 문자열을 유지하기 위해 baseUrl 대신 req.originalUrl을 사용하고 싶지만 이것이 항상 원하는 동작은 아닙니다.
MaxPRafferty 2016 년

@MaxPRafferty-확실한 의견. 가치가 없다. 감사.
이기적인

4
이것이 가장 좋은 해결책입니다. http-proxy-middleware를 사용하고 있지만 같은 개념입니다. 이미 훌륭한 솔루션이있을 때 자신의 프록시 솔루션을 돌리지 마십시오.
Andrew

1
@tannerburton 감사합니다! 나는 대답을 업데이트했다.
이기적인

46

POST로 작업 하기 위해 trigoman 의 답변 (전체 크레딧) 을 확장하려면 (PUT 등으로 작업 할 수도 있음) :

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
PUT에서 작동하지 못했습니다. 그러나 GET 및 POST에는 효과적입니다. 감사합니다!!
Mariano Desanze

5
PUT 요청을위한 @Protron은 다음과 같은 것을 사용합니다.if(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
davnicwil

PUT 또는 POST 요청의 일부로 헤더를 통과해야하는 경우 요청이 계산할 수 있도록 컨텐츠 길이 헤더를 삭제하십시오. 그렇지 않으면 수신 서버가 데이터를자를 수있어 오류가 발생합니다.
Carlos Rymer

@Henrik Peinar, 이것은 로그인 게시 요청을하고 web.com/api/login에서 web.com/으로 리디렉션 할 때 도움이됩니다
valik

22

다음 설정을 사용하여 모든 것을 /rest백엔드 서버 (포트 8080)로 보내고 다른 모든 요청을 프론트 엔드 서버 (포트 3001의 웹팩 서버)로 보냅니다. 모든 HTTP 메소드를 지원하고 요청 메타 정보를 잃지 않으며 웹 소켓을 지원합니다 (핫 리로드에 필요함)

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
이것은 웹 소켓을 다루는 유일한 것입니다.
sec0ndHand

11

먼저 express 및 http-proxy-middleware를 설치하십시오.

npm install express http-proxy-middleware --save

그런 다음 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');
});

이 예에서는 포트 3000에서 사이트를 제공하지만 / api로 요청이 끝나면 localhost : 8080으로 리디렉션합니다.

http : // localhost : 3000 / api / login http : // localhost : 8080 / api / login으로 리디렉션


6

다음은 require ( 'request') npm 모듈과 하드 코딩 된 프록시 대신 환경 변수를 사용하여 복사 준비가 된 답변입니다.

커피 스크립트

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

자바 스크립트 :

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
다른 요청 함수를 사용하는 것을 제외하고는 모두 동일한 일을 수행하는 case 문 대신 먼저 살균 할 수 있습니다 (예 : 메서드가 승인 된 메서드 목록에없는 경우 기본값을 호출하는 if 문). r = 요청 [방법] (/ * 나머지 * /);
Paul

2

내가 원하는 것을 정확하게 수행하는 더 짧은 솔루션을 찾았습니다. https://github.com/http-party/node-http-proxy를

설치 후 http-proxy

npm install http-proxy --save

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: ['/']
}));

나는이 문제를 피하기 위해 사방을 며칠 동안 보냈고 많은 해결책을 시도했지만 그중 아무것도 작동하지 않았습니다.

그것이 다른 누군가를 도울 수 있기를 바랍니다 :)


0

익스프레스 샘플이 없지만 일반 http-proxy패키지 가있는 샘플이 없습니다 . 내 블로그에 사용한 프록시 버전이 매우 적습니다.

요컨대, 모든 nodejs http 프록시 패키지는 tcp (소켓) 레벨이 아닌 http 프로토콜 레벨에서 작동합니다. 이는 모든 Express 미들웨어에도 해당됩니다. 투명 프록시 나 NAT를 수행 할 수없는 것은 백엔드 웹 서버로 전송되는 패킷에 들어오는 트래픽 소스 IP를 유지하는 것을 의미합니다.

그러나 웹 서버는 http x 전달 헤더에서 원본 IP를 가져 와서 로그에 추가 할 수 있습니다.

xfwd: trueproxyOption에이블 x- 전달 헤더 기능입니다 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();

X-Forwarded Header에 대한 참조 : https://en.wikipedia.org/wiki/X-Forwarded-For

내 프록시의 정식 버전 : https://github.com/J-Siu/ghost-https-nodejs-proxy

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.