web-app 本機 API proxy

這份文件回答什麼

這份文件說明 web-app 本機開發時,如何只讓指定 API path 走本機 API server,其它 API 照常走 staging。

這不是 API mapping 文件,也不是後端路由文件;要查 endpoint 本身的業務意義,請回到API 路由與遷移或 API detail 文件。

使用情境

本機跑前端:

make dev

預設 API 會打:

https://api-staging.pro360.com.tw

如果只想讓部分 API 改打本機後端,例如:

http://127.0.0.1:12351

就使用本機 API proxy。

白話概念

這套機制分成兩段:

瀏覽器前端 JavaScript
  先把指定 staging API URL 改成本機同源 path
 
webpack dev server,也就是 Node
  再把這個本機同源 path 轉送到本機 API server

例如原本前端要打:

https://api-staging.pro360.com.tw/quote_categories/auto_complete/清潔/consumer.json

如果這個 path 有被加進 proxy 清單,瀏覽器端會先改成:

/quote_categories/auto_complete/清潔/consumer.json

所以 Chrome Network 看到的 Request URL 會變成:

http://127.0.0.1:3000/quote_categories/auto_complete/清潔/consumer.json

接著 webpack dev server 再把它轉送到:

http://127.0.0.1:12351/quote_categories/auto_complete/清潔/consumer.json

這樣做的目的,是讓瀏覽器以為 request 是打同一個來源 127.0.0.1:3000,避免 CORS 問題;真正打到本機 API server 的動作交給 webpack dev server 處理。

流程圖

使用者打開 http://127.0.0.1:3000
        |
        v
public/index.html
        |
        |  先載入
        v
/local-api-proxy-runtime.js
        |
        |  攔截點 1:很早載入的外部 script
        |  攔截 window.fetch / XMLHttpRequest.open
        v
webkit-staging-latest.js
        |
        |  如果它打 staging API,會先被改成 /xxx
        v
 
前端 app bundle 載入
        |
        v
index.js
        |
        |  installLocalApiProxyRewrite()
        v
modules/utils/local-api-proxy.js
        |
        |  攔截點 2:前端 app 裡的 fetch / XMLHttpRequest
        |  把指定 staging API 改成同源 path
        v
 
APIManager 發 API
modules/utils/api-manager.js
        |
        |  攔截點 3:APIManager 裡的 fetch 包一層
        |  rewriteLocalApiProxyUrl(url)
        v
 
原本:
https://api-staging.pro360.com.tw/quote_categories/auto_complete/清潔/consumer.json
 
改成:
/quote_categories/auto_complete/清潔/consumer.json
        |
        v
 
瀏覽器實際送出:
http://127.0.0.1:3000/quote_categories/auto_complete/清潔/consumer.json
        |
        v
 
webpack dev server
webpack.config.js
        |
        |  proxy 設定
        v
 
實際轉送到:
http://127.0.0.1:12351/quote_categories/auto_complete/清潔/consumer.json

攔截點在哪裡

攔截點 1:外部 webkit script 載入前

位置:

/Users/mattsu/Documents/Site/web-app/public/index.html
/Users/mattsu/Documents/Site/web-app/webpack.config.js

public/index.html 先載入:

<script src="/local-api-proxy-runtime.js"></script>

然後才載入外部 webkit script。

/local-api-proxy-runtime.js 是由 webpack.config.js 在本機開發時產生的。它會先改寫:

window.fetch
window.XMLHttpRequest.prototype.open

這是為了處理 React app 還沒啟動前,外部 script 就先發 API 的情況。

攔截點 2:React app 啟動後

位置:

/Users/mattsu/Documents/Site/web-app/index.js
/Users/mattsu/Documents/Site/web-app/modules/utils/local-api-proxy.js

index.js 會執行:

installLocalApiProxyRewrite();

這段是跑在瀏覽器,不是 Node。它同樣會改寫瀏覽器裡的:

window.fetch
window.XMLHttpRequest.prototype.open

攔截點 3:APIManager 裡的 fetch

位置:

/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js

APIManager 裡的 fetch 會先經過:

rewriteLocalApiProxyUrl(url)

這是保險的一層。只要 APIManager 用這個 fetch 發 API,URL 就會先被檢查一次。

誰跑在瀏覽器,誰跑在 Node

跑在瀏覽器:
index.js
modules/utils/local-api-proxy.js
modules/utils/api-manager.js
public/index.html 載入的 /local-api-proxy-runtime.js
 
跑在 Node:
webpack.config.js
webpack dev server proxy

啟動方式

cd /Users/mattsu/Documents/Site/web-app
LOCAL_API_PROXY=http://127.0.0.1:12351 make dev

沒有帶 LOCAL_API_PROXY 時:

make dev

會維持原本行為,API 照常打 staging。

新增或移除 proxy API

只改這個檔案:

/Users/mattsu/Documents/Site/web-app/config/local-api-proxy.js

格式:

const LOCAL_API_PROXY_ENV = 'LOCAL_API_PROXY';
 
const localApiProxyPaths = [];
 
module.exports = {
  LOCAL_API_PROXY_ENV,
  localApiProxyPaths,
};

新增

把 API path 前綴加進 localApiProxyPaths

const localApiProxyPaths = [
  '/quote_categories/auto_complete',
  '/users/validate_user.json',
  '/new/v1/something',
];

移除

localApiProxyPaths 刪掉該 path:

const localApiProxyPaths = [
  '/quote_categories/auto_complete',
];

如果清單是空的:

const localApiProxyPaths = [];

即使有帶 LOCAL_API_PROXY,也不會有 API 被轉到本機。

path 規則

清單放的是「API path 前綴」,不是完整 URL。

例如放:

'/quote_categories/auto_complete'

會套用到:

/quote_categories/auto_complete/清潔/consumer.json
/quote_categories/auto_complete//consumer.json

不會影響其它 API。

Network 怎麼看

成功時,Chrome DevTools Network 會看到 request 變成同源:

http://127.0.0.1:3000/quote_categories/auto_complete/清潔/consumer.json

實際上會由 webpack dev server proxy 到:

http://127.0.0.1:12351/quote_categories/auto_complete/清潔/consumer.json

如果 Network 還看到:

https://api-staging.pro360.com.tw/quote_categories/auto_complete/...

先確認:

  1. 是否用 LOCAL_API_PROXY=http://127.0.0.1:12351 make dev 啟動。
  2. 是否已 hard reload,或 DevTools 是否勾選 Disable cache。
  3. 該 path 是否已加到 config/local-api-proxy.js

目前預設

config/local-api-proxy.js 預設不代理任何 API:

const localApiProxyPaths = [];

需要時再把 path 加進去。