點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Express 實現 Proxy Server

Sam Xiao's Avatar 2021-10-24

Node 除了能實現 HTTP Server 外,只要加上 Http-proxy middleware,也能輕易實現 Proxy Server。

Version

Node 12.4
Express 4.17.1
http-proxy-middleware 0.19.1

Add Package

$ yarn add express body-parser http-proxy-middleware
  • express:web framework
  • body-parser:讀取 HTTP POST body
  • http-proxy-middleware:負責 proxy

Proxy Server

let express = require ('express');
let bodyParser = require ('body-parser');
let proxy = require ('http-proxy-middleware');

let webServer = express ()
let apiServer = express ()

apiServer.use (bodyParser.urlencoded ({ extended: true }))

apiServer.post ('/users', (req, res) => {
  let id = req.body.id
  let name = req.body.name
  let msg = `${id}: ${name}`

  res.send(msg);
})

webServer.use ( 
  '/api',
  proxy ({
    target: 'http://localhost:3000/',
    changeOrigin: true,
    pathRewrite: {
      '^/api/': '/'
    }
  })
)

webServer.listen(80, _ => console.log('Web server listening on port 80'))
apiServer.listen(3000, _ => console.log('API server listening on port 3000'))

實務上會使用 proxy,最常見是因為 client 直接打不同 domain 的 API server 導致 CORS。

若 API 我們自己可以掌控,固然可直接改 code 開放 CORS;或者先打到自己的 Web server,再透過 proxy 到 API server 躲過 CORS。

第 5 行

let webServer = express ()
let apiServer = express ()

webServer.listen (80, _ => console.log('Web server listening on port 80'))
apiServer.listen (3000, _ => console.log('API server listening on port 3000'))

分別使用 express 建立兩個 HTTP server:

  • webServer:port 為 80
  • apiServer:port 為 3000

Web server 與 API server 雖然同屬相同 host name 與 IP,但因為 port 不同,browser 已經視為 CORS。

第 8 行

apiServer.use (bodyParser.urlencoded ({ extended: true }))

API server 使用 bodyParser middleware。

第 10 行

apiServer.post ('/users', (req, res) => {
  let id = req.body.id
  let name = req.body.name
  let msg = `${id}: ${name}`

  res.send(msg)
})

API server 接受 /users 的 POST request,其 body 為 idname

18 行

webServer.use (
  '/api',
  proxy ({
    target: 'http://localhost:3000/',
    changeOrigin: true,
    pathRewrite: {
      '^/api/': '/'
    }
  })
)

實務上常將原本外部 API 的 URL 改打自己 Web server 的 /api

如原本為 http://localhost:3000/users POST:

proxy000

改成 http://localhost:80/api/users POST。

proxy001

webServer.use('/api', proxy(...));

use 的第一個 argument 為 context matching,只要符合 matching,就會繼續 proxy。

注意並不是如 Vue CLI 3 為 mapping,將稍後設定

proxy({
  target: 'http://localhost:3000/',
  changeOrigin: true,
  pathRewrite: {
    '^/api/': '/'
  }
})

設定 proxy option:

  • target:為 API server
  • changeOrigin:設定 true 將 host header 的 origin 改成 target URL
  • pathRewrite:任何 api 開頭的 request 將變成 /

Conclusion

  • 實務上可將 Vue 包在同一個 Node + Express + Http-proxy 的 Docker image 內,如此不需要 Nginx 也能有 proxy

Reference

Chimera, http-proxy-middleware