開發自訂外掛程式

查看 Apigee Edge 說明文件。
前往 Apigee X說明文件
資訊

Edge Microgateway 2.4.x 版

目標對象

本主題適用於希望以書面方式擴充 Edge Microgateway 功能的開發人員 自訂外掛程式如想編寫新的外掛程式,具備 JavaScript 和 Node.js 的經驗 這通常代表交易 不會十分要求關聯語意

什麼是自訂 Edge Microgateway 外掛程式?

外掛程式是一種 Node.js 模組,可為 Edge Microgateway 新增功能。外掛程式模組 遵循一致模式,並儲存在 Edge Microgateway 上的已知位置,藉此達成目的 以便自動探索及執行當您下載容器時 安裝 Edge Microgateway。這些外掛程式包括各種外掛程式 數據分析如需這些現有外掛程式的說明,請參閱「使用外掛程式」。

您可以為微門安裝新功能,例如:自訂 外掛程式」一文。根據預設,Edge Microgateway 基本上就是安全直通 Proxy, 會傳送與目標服務未經變更的要求和回應。使用自訂外掛程式 透過程式與進入微門閘道的要求和回應互動。

自訂外掛程式程式碼的位置

Edge Microgateway 安裝過程中會包含自訂外掛程式的資料夾 這裡:

[prefix]/npm/lib/node_modules/edgemicro/plugins

其中 [prefix]npm 前置字串目錄,如下所示: 如「Edge Microgateway 的安裝位置」中所述說明如何安裝 Edge Microgateway

如要變更這個預設外掛程式目錄,查看目的地 尋找外掛程式

查看預先定義的外掛程式

在嘗試自行開發外掛程式之前,建議先檢查 即可滿足各種需求這些外掛程式位於:

[prefix]/npm/lib/node_modules/edgemicro/node_modules/microgateway-plugins

其中 [prefix]npm 前置字串目錄。詳情請見 以及「Edge Microgateway 安裝在哪裡」於「安裝 Edge 中 Microgateway

詳情請參閱「預先定義 隨附的外掛程式

編寫簡易外掛程式

本節將逐步引導您建立簡易外掛程式。這個外掛程式 會以「Hello, World!」字串覆寫回應資料 (無論是什麼情況)。然後輸出到 終端機。

  1. 如果 Edge Microgateway 正在執行,請立即停止:
    edgemicro stop
  2. cd 至自訂外掛程式目錄:

    cd [prefix]/npm/lib/node_modules/edgemicro/plugins

    其中 [prefix]npm 前置字串目錄 ,如同「Edge Microgateway 的安裝位置」一節中所述說明如何安裝 Edge Microgateway

  3. 建立新的外掛程式專案 對其稱為 Response-override,以及 cd
    mkdir response-override && cd response-override
  4. 建立新的 Node.js 專案:
    npm init
    點按「多次返回」,即可接受預設值。
  5. 使用文字編輯器建立名為 index.js 的新檔案。
  6. 將下列程式碼複製到 index.js,並儲存檔案。
    'use strict';
    var debug = require('debug')
    
    module.exports.init = function(config, logger, stats) {
    
      return {
       
        ondata_response: function(req, res, data, next) {
          debug('***** plugin ondata_response');
          next(null, null);
        },
        
        onend_response: function(req, res, data, next) {
          debug('***** plugin onend_response');
          next(null, "Hello, World!\n\n");
        }
      };
    }
    敬上
  7. 您已建立外掛程式,並將外掛程式新增至 Edge Microgateway 設定。 開啟「$HOME/.edgemicro/[org]-[env]-config.yaml」檔案。 其中 orgenv 是您的 Edge 機構。 環境名稱
  8. response-override 外掛程式新增至 plugins:sequence 元素,如下所示。
          ...
          
          plugins:
            dir: ../plugins
            sequence:
              - oauth
              - response-override
              
          ...
        
    敬上
  9. 重新啟動 Edge Microgateway。
  10. 透過 Edge Microgateway 呼叫 API。(此 API 呼叫假設您已設定 與 API 金鑰安全性教學課程所述,進行設定,如設定 設定 Edge Microgateway
    curl -H 'x-api-key: uAM4gBSb6YoMvTHfx5lXJizYIpr5Jd' http://localhost:8000/hello/echo
    Hello, World!

外掛程式剖析

下列 Edge Microgateway 範例外掛程式說明 開發您自己的外掛程式本節探討的範例外掛程式原始碼為 plugins/header-uppercase/index.js.

  • 外掛程式是標準的 NPM 模組, 根層級的 package.jsonindex.js 資料夾。
  • 外掛程式必須匯出 init() 函式。
  • init() 函式 引數:configlogger、 和 stats 的統計資料。這些引數 外掛程式 init() 函式引數。
  • init() 會傳回含有呼叫函式處理常式的物件 在要求的生命週期內發生某些事件時。

事件處理常式函式

外掛程式必須實作部分或所有的事件處理常式函式。實作前述項目 功能由您決定任何指定的函式都屬於選用性質,一般外掛程式會在 也就是這些函式的子集

要求流程事件處理常式

系統會在 Edge Microgateway 中的要求事件上呼叫這些函式。

  • onrequest
  • ondata_request
  • onend_request
  • onclose_request
  • onerror_request

onrequest 函式

在用戶端要求開始時呼叫。這個函式會在 要求。這個函式可讓您存取要求標頭 網址、查詢參數和 HTTP 方法如果您接著呼叫了 錯誤的執行個體),則要求會停止處理程序,而且目標要求不會開始。

範例:

onrequest: function(req, res, next) {
      debug('plugin onrequest');
      req.headers['x-foo-request-start'] = Date.now();
      next();
    }

ondata_request 函式

從用戶端接收到資料區塊時呼叫。將要求資料傳送至 外掛程式和外掛程式系統會將序列中最後一個外掛程式傳回的值傳送至 常見的用途是先轉換要求資料,然後再傳送要求資料 (如下所示) 然後直接傳送給目標

範例:

ondata_request: function(req, res, data, next) {
      debug('plugin ondata_request ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    }

onend_request 函式

收到用戶端的所有要求資料時,會呼叫此方法。

範例:

onend_request: function(req, res, data, next) {
      debug('plugin onend_request');
      next(null, data);
    }

onclose_request 函式

表示用戶端連線已關閉。在以下情況下,您可以使用此函式 用戶端連線不穩定。當通訊端與用戶端的通訊端連線 已打烊。

範例:

onclose_request: function(req, res, next) {
      debug('plugin onclose_request');
      next();
    }

onerror_request 函式

如果收到用戶端要求時發生錯誤,就會呼叫此方法。

範例:

onerror_request: function(req, res, err, next) {
      debug('plugin onerror_request ' + err);
      next();
    }

回應流程事件處理常式

系統會在 Edge Microgateway 中的回應事件上呼叫這些函式。

  • onresponse
  • ondata_response
  • onend_response
  • onclose_response
  • onerror_response

onresponse 函式

在目標回應開始時呼叫。這個函式會在 則 Edge Microgateway 會收到回應。這個函式可讓您存取回應標頭 和狀態碼

範例:

onresponse: function(req, res, next) {      
    debug('plugin onresponse');     
    res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start'])    
    next();    
}


ondata_response 函式

從目標收到資料區塊時呼叫。

範例:

ondata_response: function(req, res, data, next) {
      debug('plugin ondata_response ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    }
敬上


onend_response 函式

從目標收到所有回應資料時,會呼叫此方法。

範例:

onend_response: function(req, res, data, next) {
      debug('plugin onend_response');
      next(null, data);
    }
敬上

onclose_response 函式

表示目標連線已關閉。在以下情況下,您可以使用此函式 目標連線不穩定。當與目標的通訊端連線 已打烊。

範例:

onclose_response: function(req, res, next) {
      debug('plugin onclose_response');
      next();
    }


onerror_response 函式

如果無法接收目標回應,就會呼叫此方法。

範例:

onerror_response: function(req, res, err, next) {
      debug('plugin onerror_response ' + err);
      next();
    }

不可不知的 外掛程式事件處理常式函式

系統會呼叫外掛程式事件處理常式函式,以回應在 Edge Microgateway 處理指定的 API 要求。

  • 每個 init() 函式處理常式 (ondata_requestondata_response 等) 必須呼叫 next() 回呼。如果您不 呼叫 next() 則停止處理,且要求會停止運作。
  • next() 的第一個引數可能是錯誤,導致 終止處理程序。
  • ondata_onend_ 處理常式必須 使用第二個引數 (包含要傳送的資料) 呼叫 next() 複製到目標或用戶端如果外掛程式正在緩衝處理,且沒有 有足夠的資料,可以進行轉換
  • 請注意,外掛程式的單一執行個體可用來處理所有要求和回應。 如果外掛程式想在兩次呼叫之間保留每個要求的狀態,該外掛程式可將該狀態儲存在 新增至提供的 request 物件 (req),其生命週期是 API 呼叫的時間長度。
  • 請務必找出所有錯誤,並呼叫包含錯誤的 next()。 未呼叫 next() 將導致 API 呼叫停止運作。
  • 請小心不要引發記憶體流失,因為這可能會影響 Edge 的整體效能 Microgateway 並在記憶體不足時造成當機。
  • 遵循 Node.js 模型時,請務必小心,不要在主目錄執行耗用大量運算資源的工作 因為這樣可能會對 Edge Microgateway 的效能造成負面影響。

關於外掛程式 init() 函式

本節會說明傳送到 init() 函式:configlogger、 和 stats 的統計資料。

config

將 Edge Microgateway 設定檔與 從 Apigee Edge 下載的資訊,例如產品和配額。您可以 這個物件中的外掛程式專屬設定:config.<plugin-name>

加入名稱為 param 的設定參數並加上值 foo 新增至 response-override 外掛程式,請將 寫入 default.yaml 檔案:

response-override:
    param: foo

接著,您可以存取外掛程式程式碼中的參數,如下所示:

// Called when response data is received
    ondata_response: function(req, res, data, next) {
      debug('***** plugin ondata_response');
      debug('***** plugin ondata_response: config.param: ' + config.param);
      next(null, data);
    },

在這種情況下,您會在外掛程式偵錯輸出內容中看到 foo 顯示的內容:

Sun, 13 Dec 2015 21:25:08 GMT plugin:response-override ***** plugin ondata_response: config.param: foo

Logger

系統記錄器。目前使用的記錄器會匯出這些函式,而物件可 字串、HTTP 要求、HTTP 回應或 Error 執行個體。

  • info(object, message)
  • warn(object, message)
  • error(object, message)

stats

保存要求、回應、錯誤及其他匯總統計資料之計數的物件 與相關要求和回應有關

  • treqErrors - 發生錯誤的目標要求數量。
  • treqErrors - 發生錯誤的目標回應數量。
  • statusCodes - 包含回應代碼計數的物件:
{
  1: number of target responses with 1xx response codes
  2: number of target responses with 2xx response codes
  3: number of target responses with 3xx response codes
  4: number of target responses with 4xx response codes
  5: number of target responses with 5xx response codes
  }
  
  • requests:要求總數。
  • responses - 回應的總數。
  • connections:有效的目標連線數。

關於 next() 函式

所有外掛程式方法都必須呼叫 next(),才能繼續處理下一項 方法 (否則外掛程式程序會停止運作)。在請求生命週期中, onrequest() 方法。接著要呼叫的方法為 ondata_request() 方法; 但「只有」在呼叫 ondata_request 時,才會呼叫 要求包含資料,就像在 POST 要求時一樣。接著呼叫 值為 onend_request(),系統會在要求處理要求時呼叫 已完成只有在發生事件發生時,才會呼叫 onerror_* 函式 並允許您視需要透過自訂程式碼處理錯誤。

假設資料在要求中傳送,並呼叫 ondata_request()。 請注意,函式會使用兩個參數呼叫 next()

next(null, data);

按照慣例,第一個參數是用來傳送錯誤資訊,然後 處理鏈結中後續函式中的錯誤。如果設為「null」,這就太可怕了 引數,表示沒有任何錯誤,要求處理將正常進行。如果 這個引數是悲觀的 (例如 Error 物件),那麼要求處理停止,而要求 傳送給目標

第二個參數將要求資料傳遞至鏈結中的下一個函式。如果您不這麼做 則會將該要求資料直接傳遞至 API 的目標。 不過,您可以在這個方法中修改要求資料,然後將修改過的 接收所有要求的 HTTP 要求例如,如果要求資料是 XML,且目標預期使用 JSON, 接著,您可以將程式碼新增至 ondata_request() 方法,以 (a) 變更 將要求標頭的 Content-Type 轉換為 application/json,並將 透過任何方式要求 JSON 資料 (例如您可以使用 從 NPM 取得的 Node.js xml2json 轉換器)。

以下是可能的模樣:

ondata_request: function(req, res, data, next) {
  debug('****** plugin ondata_request);
  var translated_data = parser.toJson(data);
  next(null, translated_data);
},

在此情況下,要求資料 (假設為 XML) 會轉換為 JSON,而 轉換的資料會透過 next() 傳遞到要求中的下一個函式 再傳遞至後端目標

請注意,您可以新增其他偵錯陳述式,用來輸出轉換後的資料進行偵錯 用途。例如:

ondata_request: function(req, res, data, next) {
  debug('****** plugin ondata_request);
  var translated_data = parser.toJson(data);
  debug('****** plugin ondata_response: translated_json: ' + translated_json);
  next(null, translated_data);
},

簡介 外掛程式處理常式執行順序

如果您編寫 Edge Microgateway 外掛程式,必須瞭解外掛程式的順序 事件處理常式的執行。

重點是,在 Edge 中指定外掛程式序列時 Microgateway 設定檔,要求處理常式會執行 依遞增順序排列,而回應處理常式在執行時 以遞減順序排列。

以下範例可協助您瞭解這個執行順序。

1. 建立三個簡易格式 外掛程式

請考慮使用下列外掛程式。而是在其事件處理常式發生時,列印控制台輸出內容 呼叫:

plugins/plugin-1/index.js

module.exports.init = function(config, logger, stats) {

  return {

    onrequest: function(req, res, next) {
      console.log('plugin-1: onrequest');
      next();
    },

    onend_request: function(req, res, data, next) {
      console.log('plugin-1: onend_request');
      next(null, data);
    },

    ondata_response: function(req, res, data, next) {
      console.log('plugin-1: ondata_response ' + data.length);
      next(null, data);
    },

    onend_response: function(req, res, data, next) {
      console.log('plugin-1: onend_response');
      next(null, data);
    }
  };
}

現在,您可以考慮再建立兩個 使用相同程式碼的外掛程式 (plugin-2plugin-3) (除了變更 console.log() 陳述式) 分別是 plugin-2plugin-3)。

2. 查看外掛程式程式碼

匯出的外掛程式函式 <microgateway-root-dir>/plugins/plugin-1/index.js 年是活動 處理常式會在要求和回應處理期間在特定時間執行。適用對象 例如,onrequest 會執行要求標頭的第一個位元組 。而 onend_response 會在回應的最後一個位元組後執行 才能接收資料。

請查看處理常式 ondata_response,每次回應資料區塊時就會呼叫此處理常式 已收到。值得注意的是,不一定每次都會收到回應資料 一次。而是可能會以任意長度的區塊接收資料。

3. 將外掛程式新增至 外掛程式序列

延續這個範例,我們會在 Edge 的外掛程式序列中新增外掛程式 Microgateway 設定檔 (~./edgemicro/config.yaml),如下所示。序列是 非常重要這會定義外掛程式處理常式的執行順序。

  plugins:
    dir: ../plugins
    sequence:
      - plugin-1
      - plugin-2
      - plugin-3
  

4. 檢查偵錯輸出內容

現在,來看看呼叫這些外掛程式時會產生的輸出內容。另有 以下是幾個重點:

  • 外掛程式會排列 Edge Microgateway 設定檔的順序 (~./edgemicro/config.yaml) 指定事件處理常式的順序 物件。
  • 要求處理常式是依遞增順序呼叫 ( 它們會顯示在外掛程式序列中 -- 1、2、3)。
  • 回應處理常式會依「遞減」的順序呼叫,例如 3、2、 1.
  • 每個資料區塊都會呼叫一次 ondata_response 處理常式 一些新星球這個範例 (如下所示的輸出內容) 會收到兩個區塊。

以下是在使用這三種外掛程式且傳送要求時產生的偵錯輸出內容範例 通過 Edge Microgateway您只需注意呼叫處理常式的順序:

  plugin-1: onrequest
  plugin-2: onrequest
  plugin-3: onrequest

  plugin-1: onend_request
  plugin-2: onend_request
  plugin-3: onend_request

  plugin-3: ondata_response 931
  plugin-2: ondata_response 931
  plugin-1: ondata_response 931

  plugin-3: ondata_response 1808
  plugin-3: onend_response

  plugin-2: ondata_response 1808
  plugin-2: onend_response

  plugin-1: ondata_response 1808
  plugin-1: onend_response

摘要

當您嘗試嘗試建立外掛程式時,必須瞭解外掛程式處理常式的呼叫順序 實作自訂外掛程式功能,例如累積及轉換要求或回應 資料。

請記得,要求處理常式會按照外掛程式執行的順序執行 Edge Microgateway 設定檔中指定的回應處理常式,且回應處理常式會在 以相反順序

關於在外掛程式中使用全域變數

傳送至 Edge Microgateway 的每項要求都會傳送到同一個外掛程式的同一個執行個體;因此, 另一個用戶端發出的第二個要求狀態就會覆寫第一個要求的狀態。唯一安全的環境 儲存外掛程式狀態的方法是將狀態儲存在要求或回應物件的 屬性中 ( 生命週期僅限於要求的時間)。

重寫外掛程式中的目標網址

已新增:v2.3.3

您可以修改這些變數,以動態方式覆寫外掛程式中的預設目標網址 外掛程式 程式碼:req.targetHostnamereq.targetPath

已新增:v2.4.x

您也可以覆寫目標端點通訊埠,然後選擇 HTTP 或 HTTPS。修改這些項目 外掛程式中的變數 程式碼:req.targetPortreq.targetSecure。目的地: 選擇 HTTPS,並將 req.targetSecure 設為 true;的 請設為 false。如果您將 req.targetSecure 設為 true, 請參閱此討論 執行緒

已將名為 eurekaclient 的範例外掛程式新增至 Edge Microgateway。這個外掛程式示範如何使用 req.targetPort 和 req.targetSecure 變數,並說明 Edge Microgateway 如何利用 Eureka 做為服務端點目錄。


外掛程式範例

這些外掛程式包含在 Edge Microgateway 安裝作業中。安裝在 在此處安裝 Edge Microgateway:

[prefix]/npm/lib/node_modules/edgemicro/plugins

其中 [prefix]npm 前置字串目錄,如下所示: 如「Edge Microgateway 的安裝位置」中所述說明如何安裝 Edge Microgateway

accumulate-request

這個外掛程式會將用戶端的資料區塊,累積到附加至 物件物件。收到所有要求資料時,陣列會串連至緩衝區 然後傳遞至序列中的下一個外掛程式這個外掛程式應為第一個外掛程式 以便後續外掛程式接收累積的要求資料。

module.exports.init = function(config, logger, stats) {

  function accumulate(req, data) {

    if (!req._chunks) req._chunks = [];
    req._chunks.push(data);

  }

  return {

    ondata_request: function(req, res, data, next) {

      if (data && data.length > 0) accumulate(req, data);

      next(null, null);

    },


    onend_request: function(req, res, data, next) {

      if (data && data.length > 0) accumulate(req, data);

      var content = null;

      if (req._chunks && req._chunks.length) {

        content = Buffer.concat(req._chunks);

      }

      delete req._chunks;

      next(null, content);

    }

  };

}

accumulate-response

這個外掛程式能將目標中的資料區塊,累積至附加至 回應物件。系統收到所有回應資料時,陣列會串連至緩衝區 然後傳遞至序列中的下一個外掛程式由於這個外掛程式 回應會反向處理,請放置做為最後一個外掛程式 序列。

module.exports.init = function(config, logger, stats) {

  function accumulate(res, data) {
    if (!res._chunks) res._chunks = [];
    res._chunks.push(data);
  }

  return {

    ondata_response: function(req, res, data, next) {
      if (data && data.length > 0) accumulate(res, data);
      next(null, null);
    },

    onend_response: function(req, res, data, next) {
      if (data && data.length > 0) accumulate(res, data);
      var content = Buffer.concat(res._chunks);
      delete res._chunks;
      next(null, content);
    }

  };

}

header-uppercase 外掛程式

Edge Microgateway 發行版包含外掛程式範例 名為 <microgateway-root-dir>/plugins/header-uppercase。範例 包含說明每個函式處理常式的註解。此範例會執行一些簡單的資料 並將自訂標頭新增至用戶端要求和目標 回應。

以下是原始碼 針對 <microgateway-root-dir>/plugins/header-uppercase/index.js

'use strict';

var debug = require('debug')('plugin:header-uppercase');

// required
module.exports.init = function(config, logger, stats) {

  var counter = 0;

  return {

    // indicates start of client request
    // request headers, url, query params, method should be available at this time
    // request processing stops (and a target request is not initiated) if
    // next is called with a truthy first argument (an instance of Error, for example)
    onrequest: function(req, res, next) {
      debug('plugin onrequest');
      req.headers['x-foo-request-id'] = counter++;
      req.headers['x-foo-request-start'] = Date.now();
      next();
    },

    // indicates start of target response
    // response headers and status code should be available at this time
    onresponse: function(req, res, next) {
      debug('plugin onresponse');
      res.setHeader('x-foo-response-id', req.headers['x-foo-request-id']);
      res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']);
      next();
    },

    // chunk of request body data received from client
    // should return (potentially) transformed data for next plugin in chain
    // the returned value from the last plugin in the chain is written to the target
    ondata_request: function(req, res, data, next) {
      debug('plugin ondata_request ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    },

    // chunk of response body data received from target
    // should return (potentially) transformed data for next plugin in chain
    // the returned value from the last plugin in the chain is written to the client
    ondata_response: function(req, res, data, next) {
      debug('plugin ondata_response ' + data.length);
      var transformed = data.toString().toUpperCase();
      next(null, transformed);
    },

    // indicates end of client request
    onend_request: function(req, res, data, next) {
      debug('plugin onend_request');
      next(null, data);
    },

    // indicates end of target response
    onend_response: function(req, res, data, next) {
      debug('plugin onend_response');
      next(null, data);
    },

    // error receiving client request
    onerror_request: function(req, res, err, next) {
      debug('plugin onerror_request ' + err);
      next();
    },

    // error receiving target response
    onerror_response: function(req, res, err, next) {
      debug('plugin onerror_response ' + err);
      next();
    },

    // indicates client connection closed
    onclose_request: function(req, res, next) {
      debug('plugin onclose_request');
      next();
    },

    // indicates target connection closed
    onclose_response: function(req, res, next) {
      debug('plugin onclose_response');
      next();
    }

  };

}

轉換大寫

這是一般轉換外掛程式,您可以修改這個外掛程式 這個範例會將回應和要求資料 大寫。

 */
module.exports.init = function(config, logger, stats) {

  // perform content transformation here
  // the result of the transformation must be another Buffer
  function transform(data) {
    return new Buffer(data.toString().toUpperCase());
  }

  return {

    ondata_response: function(req, res, data, next) {
      // transform each chunk as it is received
      next(null, data ? transform(data) : null);
    },

    onend_response: function(req, res, data, next) {
      // transform accumulated data, if any
      next(null, data ? transform(data) : null);
    },

    ondata_request: function(req, res, data, next) {
      // transform each chunk as it is received
      next(null, data ? transform(data) : null);
    },

    onend_request: function(req, res, data, next) {
      // transform accumulated data, if any
      next(null, data ? transform(data) : null);
    }

  };

}

其他外掛程式教學課程

另請參閱 Apigee 網誌上的這些教學課程: