開發自訂外掛程式

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

Edge Microgateway 3.0.x 版

目標對象

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

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

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

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

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

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

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

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

如要變更這個預設外掛程式目錄,查看所在位置 外掛程式」一文。

查看預先定義的外掛程式

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

[prefix]/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]/lib/node_modules/edgemicro/plugins

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

  3. 建立名為 response-overridecd 的新外掛程式專案 至:
    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 金鑰安全性」教學課程一樣,瞭解如何設定 API 金鑰安全性 並設定 Edge Microgateway
    curl -H 'x-api-key: uAM4gBSb6YoMvTHfx5lXJizYIpr5Jd' http://localhost:8000/hello/echo
    Hello, World!
    

外掛程式剖析

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

  • 外掛程式是具有 package.jsonindex.js 的標準 NPM 模組 複製到根資料夾
  • 外掛程式必須匯出 init() 函式。
  • init() 函式會使用三個引數:configloggerstats。外掛程式會詳細說明這些引數 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() 函式的引數: configloggerstats

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 檔 (例如使用 Node.js 從 NPM 取得的 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-2 以外) 和 plugin-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]/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);
    }

  };

}