Azure Functions Node.js 開發人員指南

本指南是使用 JavaScript 或 TypeScript 開發 Azure Functions 的簡介。 本文假設您已經讀過 Azure Functions 開發人員指南

重要

本文的內容會根據您在頁面頂端選取器中選擇的 Node.js 程式設計模型而有所不同。 您所選的版本應該符合您在應用程式中所使用的 @azure/functions npm 套件版本。 如果 package.json 中未列出該套件,則預設值為 v3。 參閱移轉指南,深入了解 v3 與 v4 之間的差異。

身為 Node.js 開發人員,您可能也對下列其中一篇文章感興趣:

開始使用 概念 引導式學習

考量

  • 不應將 Node.js 程式設計模型與 Azure Functions 執行階段混淆:
    • 程式設計模型:定義您撰寫程式碼的方式,以及 JavaScript 和 TypeScript 特有的方式。
    • 執行階段:定義 Azure Functions 的基礎行為,並跨所有語言共用。
  • 程式設計模型的版本會嚴格地繫結至 @azure/functions npm 套件版本。 此為獨立於執行階段的版本。 執行階段和程式設計模型都使用數字 4 作為其最新的主要版本,但這是巧合。
  • 您無法在相同的函數應用程式中混合使用 v3 和 v4 程式設計模型。 一旦您在應用程式中註冊一個 v4 函式,function.json 檔案中註冊的任何 v3 函式就會遭到忽略。

支援的版本

下表顯示 Node.js 程式設計模型的每個版本,以及其支援的 Azure Functions 執行階段和Node.js 版本。

程式設計模型版本 支援層級 函式執行階段版本 Node.js 版本 描述
4.x GA 4.25+ 20.x、18.x 支援彈性的檔案結構和以程式碼為中心的觸發程序和繫結方法。
3.x GA 4.x 20.x、18.x、16.x、14.x 需要具有在 "function.json" 檔案中宣告之觸發程序和繫結的特定檔案結構
2.x n/a 3.x 14.x、12.x、10.x 已於 2022 年 12 月 13 日終止支援。 如需詳細資訊,請參閱函式版本
1.x n/a 2.x 10.x、8.x 已於 2022 年 12 月 13 日終止支援。 如需詳細資訊,請參閱函式版本

資料夾結構

JavaScript 專案的必要資料夾結構如下範例所示:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - myFirstFunction/
 | | - index.js
 | | - function.json
 | - mySecondFunction/
 | | - index.js
 | | - function.json
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

主要專案資料夾 (<project_root>) 可以包含下列檔案:

  • .vscode/:(選用) 包含已儲存的 Visual Studio Code 設定。 若要深入了解,請參閱 Visual Studio Code 設定
  • myFirstFunction/function.json:包含函式觸發程序、輸入和輸出的設定。 目錄的名稱會決定函式的名稱。
  • myFirstFunction/index.js:儲存函式程式碼。 若要變更此預設檔案路徑,請參閱使用 scriptFile
  • .funcignore:(選擇性) 宣告不應發佈至 Azure 的檔案。 此檔案通常包含 .vscode/ 來忽略編輯器設定、test/ 來忽略測試案例,以及 local.settings.json 來防止正在發佈的本地應用程式設定。
  • host.json:包含會對函數應用程式執行個體中所有函式產生影響的設定選項。 此檔案會發行至 Azure。 在本機執行時,不支援所有選項。 若要深入了解,請參閱host.json
  • local.settings.json:可在本機執行時用來儲存應用程式設定和連接字串。 此檔案不會發行至 Azure。 若要深入瞭解,請參閱 local.settings.file
  • package.json:包含設定選項,例如套件相依性清單、主要進入點和指令碼。

JavaScript 專案的建議資料夾結構如下範例所示:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - src/
 | | - functions/
 | | | - myFirstFunction.js
 | | | - mySecondFunction.js
 | - test/
 | | - functions/
 | | | - myFirstFunction.test.js
 | | | - mySecondFunction.test.js
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

主要專案資料夾 (<project_root>) 可以包含下列檔案:

  • .vscode/:(選用) 包含已儲存的 Visual Studio Code 設定。 若要深入了解,請參閱 Visual Studio Code 設定
  • src/functions/:這是所有函式及其相關觸發程序和繫結的預設位置。
  • test/:(選用) 包含函數應用程式的測試案例。
  • .funcignore:(選擇性) 宣告不應發佈至 Azure 的檔案。 此檔案通常包含 .vscode/ 來忽略編輯器設定、test/ 來忽略測試案例,以及 local.settings.json 來防止正在發佈的本地應用程式設定。
  • host.json:包含會對函數應用程式執行個體中所有函式產生影響的設定選項。 此檔案會發行至 Azure。 在本機執行時,不支援所有選項。 若要深入了解,請參閱host.json
  • local.settings.json:可在本機執行時用來儲存應用程式設定和連接字串。 此檔案不會發行至 Azure。 若要深入瞭解,請參閱 local.settings.file
  • package.json:包含設定選項,例如套件相依性清單、主要進入點和指令碼。

註冊函式

v3 模型會根據兩個檔案的存在來註冊函式。 首先,您需要 function.json 檔案,該檔案位於應用程式根目錄下一層的資料夾。 其次,您需要匯出函式的 JavaScript 檔案。 根據預設,此模型會在與 function.json 相同的資料夾中尋找 index.js檔案。 如果您使用的是 TypeScript,則必須使用 function.json 中的 scriptFile 屬性,以指向已編譯的 JavaScript 檔案。 若要自訂檔案位置,或匯出函式的名稱,請參閱設定函式進入點

您匯出的函式應該一律宣告為 v3 模型中的 async function。 您可以匯出同步函式,但接著您必須呼叫 context.done(),表示函式已完成,但此函式已遭取代,不建議這麼做。

函式會傳遞叫用context作為第一個引數,以及輸入作為其餘引數。

下列範例說明的簡單函式會記錄其已遭到觸發,並使用 Hello, world! 回應:

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "authLevel": "anonymous",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
module.exports = async function (context, request) {
    context.log('Http function was triggered.');
    context.res = { body: 'Hello, world!' };
};

程式設計模型會根據 package.json 中的 main 欄位載入函式。 您可以使用 glob 模式,將 main 欄位設為單一檔案或多個檔案。 下表顯示 main 欄位的範例值:

範例 描述
src/index.js 從單一根檔案註冊函式。
src/functions/*.js 從自己的檔案註冊每個函式。
src/{index.js,functions/*.js} 組合可讓您從自己的檔案註冊每個函式,但您仍有一般應用層級程式碼的根檔案。

若要註冊函式,您必須從 @azure/functions npm 模組匯入 app 物件,並呼叫觸發程序類型特有的方法。 註冊函式時的第一個引數是函式名稱。 第二個引數是 options 物件,其會指定觸發程序、處理常式和任何其他輸入或輸出的設定。 在某些不需要觸發程序設定的案例中,您可以直接傳遞處理常式作為第二個引數,而不是 options 物件。

只要根據 package.json 檔案中的 main 欄位載入 (直接或間接) 該檔案,即可從專案中的任何檔案註冊函式。 應該在全域範圍註冊函式,因為一旦開始執行,就無法註冊函式。

下列範例說明的簡單函式會記錄其已遭到觸發,並使用 Hello, world! 回應:

const { app } = require('@azure/functions');

app.http('helloWorld1', {
    methods: ['POST', 'GET'],
    handler: async (request, context) => {
        context.log('Http function was triggered.');
        return { body: 'Hello, world!' };
    }
});

Inputs and outputs

函式必須有一個名為觸發程序的主要輸入。 其也可能有次要輸入和/或輸出。 輸入和輸出的設定會在 function.json 檔案中進行,兩者也稱為繫結

輸入

輸入是將 direction 設為 in 的繫結。 觸發程序和次要輸入之間的主要差異在於,觸發程序的 type 會以 Trigger 結束,例如類型 blobTrigger 與類型 blob。 大部分的函式只會使用觸發程序,而且不支援許多次要輸入類型。

這可以透過數種方式來存取輸入:

  • [建議] 作為傳遞至函式的引數:使用引數的順序與在 function.json 中定義的引數順序相同。 function.json 中定義的 name 屬性不需要符合引數的名稱,不過為了方便整理,建議您這麼做。

    module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
    
  • 作為 context.bindings 的屬性:使用索引鍵,其會符合 function.json 中定義的 name 屬性。

    module.exports = async function (context) {
        context.log("This is myTrigger: " + context.bindings.myTrigger);
        context.log("This is myInput: " + context.bindings.myInput);
        context.log("This is myOtherInput: " + context.bindings.myOtherInput);
    };
    

輸出

輸出是 direction 設為 out 的繫結,而且可以透過數種方式設定:

  • [建議用於單一輸出] 直接傳回值:如果您使用非同步函式,則可以直接傳回值。 如下列範例所示,您必須將輸出繫結的 name 屬性變更為 function.json 中的 $return

    {
        "name": "$return",
        "type": "http",
        "direction": "out"
    }
    
    module.exports = async function (context, request) {
        return {
            body: "Hello, world!"
        };
    }
    
  • [建議用於多個輸出] 傳回包含所有輸出的物件:如果您使用非同步函式,則可以傳回物件,內含符合 function.json 中每個繫結名稱的屬性。 下列範例使用名為 "httpResponse" 和 "queueOutput" 的輸出繫結:

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        return {
            httpResponse: {
                body: message
            },
            queueOutput: message
        };
    };
    
  • context.bindings 上設定值:如果您不使用非同步函式,或不想使用先前的選項,則可以直接在 context.bindings上設定值,其中索引鍵符合繫結的名稱。 下列範例使用名為 "httpResponse" 和 "queueOutput" 的輸出繫結:

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        context.bindings.httpResponse = {
            body: message
        };
        context.bindings.queueOutput = message;
    };
    

繫結資料類型

您可以在輸入繫結上使用 dataType 屬性來變更輸入的類型,但有一些限制:

  • 在 Node.js 中,僅支援 stringbinary (不支援 stream)
  • 對於 HTTP 輸入,dataType 屬性會遭到忽略。 請改用 request 物件上的屬性,以您想要的格式取得主體。 如需詳細資訊,請參閱 HTTP 要求

儲存體佇列觸發程序的下列範例中,myQueueItem 的預設類型是 string,但如果您將 dataType 設為 binary,則類型會變更為 Node.js Buffer

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "queueName": "helloworldqueue",
    "connection": "storage_APPSETTING",
    "dataType": "binary"
}
const { Buffer } = require('node:buffer');

module.exports = async function (context, myQueueItem) {
    if (typeof myQueueItem === 'string') {
        context.log('myQueueItem is a string');
    } else if (Buffer.isBuffer(myQueueItem)) {
        context.log('myQueueItem is a buffer');
    }
};

函式必須有一個名為觸發程序的主要輸入。 其也可能有次要輸入、名為傳回輸出的主要輸出和/或次要輸出。 在 Node.js 程式設計模型的內容之外,輸入和輸出也稱為繫結。 在模型的 v4 之前,這些繫結的設定是在 function.json 檔案中進行。

觸發程序輸入

觸發程序是唯一必要的輸入或輸出。 針對大部分的觸發程序類型,您會在以觸發程序類型命名的 app 物件上使用方法來註冊函式。 您可以直接在 options 引數上指定觸發程序特有的設定。 例如,HTTP 觸發程序可讓您指定路由。 在執行期間,會將對應至此觸發程序的值當作處理常式的第一個引數傳入。

const { app } = require('@azure/functions');

app.http('helloWorld1', {
    route: 'hello/world',
    handler: async (request, context) => {
        ...
    }
});

傳回輸出

傳回輸出是選用的,在某些案例中預設已設定。 例如,使用 app.http 註冊的 HTTP 觸發程序已設定為自動傳回 HTTP 回應輸出。 針對大部分的輸出類型,您可以藉助從 @azure/functions 模組匯出的 output 物件在 options 引數上指定傳回設定。 在執行期間,您會從處理常式傳回此輸出來設定此輸出。

下列範例使用計時器觸發程序儲存體佇列輸出

const { app, output } = require('@azure/functions');

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.storageQueue({
        connection: 'storage_APPSETTING',
        ...
    }),
    handler: (myTimer, context) => {
        return { hello: 'world' }
    }
});

額外的輸入和輸出

除了觸發程序和傳回之外,您也可以在註冊函式時,在 options 引數上指定額外的輸入或輸出。 從 @azure/functions 模組匯出的 inputoutput 物件提供類型特定方法,以協助建構設定。 在執行期間,您會使用 context.extraInputs.getcontext.extraOutputs.set取得或設定值,並傳入原始設定物件作為第一個引數。

下列範例是由儲存體佇列所觸發的函式,其中會將額外的儲存體 Blob 輸入複製到額外的儲存體 Blob 輸出。 佇列訊息應該是檔案的名稱,並藉助繫結運算式的協助,將 {queueTrigger} 取代為要複製的 Blob 名稱。

const { app, input, output } = require('@azure/functions');

const blobInput = input.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}',
});

const blobOutput = output.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}-copy',
});

app.storageQueue('copyBlob1', {
    queueName: 'copyblobqueue',
    connection: 'storage_APPSETTING',
    extraInputs: [blobInput],
    extraOutputs: [blobOutput],
    handler: (queueItem, context) => {
        const blobInputValue = context.extraInputs.get(blobInput);
        context.extraOutputs.set(blobOutput, blobInputValue);
    }
});

一般輸入和輸出

@azure/functions 模組所匯出的 apptriggerinputoutput 物件會提供大部分類型的類型特定方法。 針對不支援的所有類型, generic 會提供方法來讓您手動指定組態。 如果您想要變更類型特定方法所提供的預設設定,也可以使用 generic 方法。

下列範例是使用泛型方法 (而非類型特定方法) 的簡單 HTTP 觸發函式。

const { app, output, trigger } = require('@azure/functions');

app.generic('helloWorld1', {
    trigger: trigger.generic({
        type: 'httpTrigger',
        methods: ['GET', 'POST']
    }),
    return: output.generic({
        type: 'http'
    }),
    handler: async (request, context) => {
        context.log(`Http function processed request for url "${request.url}"`);

        return { body: `Hello, world!` };
    }
});

叫用內容

會為函式的每個叫用傳遞叫用 context 物件,該物件會用來讀取輸入、設定輸出、寫入記錄,以及讀取各種中繼資料。 在 v3 模型中,內容物件一律是傳遞至處理常式的第一個引數。

context 物件具有下列屬性:

屬性 說明
invocationId 目前函式叫用的識別碼。
executionContext 請參閱執行內容
bindings 請參閱繫結
bindingData 此叫用之觸發程序輸入的相關中繼資料,不包括值本身。 例如,事件中樞觸發程序具有 enqueuedTimeUtc 屬性。
traceContext 分散式追蹤的內容。 如需詳細資訊,請參閱Trace Context
bindingDefinitions function.json 中所定義,輸入和輸出的設定。
req 請參閱 HTTP 要求
res 請參閱 HTTP 回應

context.executionContext

context.executionContext 物件具有下列屬性:

屬性 說明
invocationId 目前函式叫用的識別碼。
functionName 正在叫用之函式的名稱。 包含 function.json 檔案的資料夾名稱會決定函式的名稱。
functionDirectory 包含 function.json 檔案的資料夾。
retryContext 請參閱重試內容

context.executionContext.retryContext

context.executionContext.retryContext 物件具有下列屬性:

屬性 說明
retryCount 代表目前重試嘗試次數的數字。
maxRetryCount 重試執行的次數上限。 -1 的值表示無限期重試。
exception 導致此重試的例外狀況。

context.bindings

context.bindings 物件會用來讀取輸入或設定輸出。 下列範例是儲存體佇列觸發程序,其會使用 context.bindings儲存體 Blob 輸入複製到儲存體 Blob 輸出。 佇列訊息的內容會藉助繫結運算式的協助,將 {queueTrigger} 取代為要複製的檔案名稱。

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "queueName": "helloworldqueue"
},
{
    "name": "myInput",
    "type": "blob",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}"
},
{
    "name": "myOutput",
    "type": "blob",
    "direction": "out",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}-copy"
}
module.exports = async function (context, myQueueItem) {
    const blobValue = context.bindings.myInput;
    context.bindings.myOutput = blobValue;
};

context.done

context.done 方法已遭取代。 支援非同步函式之前,您會透過呼叫 context.done() 來表示函式已完成:

module.exports = function (context, request) {
    context.log("this pattern is now deprecated");
    context.done();
};

現在,建議您移除對 context.done() 的呼叫,並將函式標示為非同步,以便傳回承諾 (即使您未await任何項目)。 一旦函式完成 (換句話說,傳回的承諾解析),v3 模型就會知道函式已完成。

module.exports = async function (context, request) {
    context.log("you don't need context.done or an awaited call")
};

會為函式的每個叫用傳遞叫用 context 物件,其中包含用於記錄的叫用和方法的相關資訊。 在 v4 模型中,context 物件通常是傳遞至處理常式的第二個引數。

InvocationContext 類別具有下列屬性:

屬性 說明
invocationId 目前函式叫用的識別碼。
functionName 函數的名稱。
extraInputs 用來取得額外輸入的值。 如需詳細資訊,請參閱額外的輸入和輸出
extraOutputs 用來設定額外輸出的值。 如需詳細資訊,請參閱額外的輸入和輸出
retryContext 請參閱重試內容
traceContext 分散式追蹤的內容。 如需詳細資訊,請參閱Trace Context
triggerMetadata 此叫用之觸發程序輸入的相關中繼資料,不包括值本身。 例如,事件中樞觸發程序具有 enqueuedTimeUtc 屬性。
options 在驗證函式之後,以及明確指定預設值的情況下,註冊函式時所使用的選項。

重試內容

retryContext 物件具有下列屬性:

屬性 說明
retryCount 代表目前重試嘗試次數的數字。
maxRetryCount 重試執行的次數上限。 -1 的值表示無限期重試。
exception 導致此重試的例外狀況。

如需詳細資訊,請參閱retry-policies

記錄

在 Azure Functions 中,建議使用 context.log() 來寫入記錄。 Azure Functions 與 Azure Application Insights 整合,以更妥善地擷取函數應用程式記錄。 Application Insights 是 Azure 監視器的一部分,提供收集、視覺化轉譯和分析應用程式記錄檔與追蹤輸出等功能。 若要深入了解,請參閱監視 Azure Functions

注意

如果您使用替代 Node.js console.log 方法,則會在應用層級追蹤這些記錄,而且這些記錄不會與任何特定函式相關聯。 強烈建議使用 context 進行記錄 (而不是 console),讓所有記錄都與特定函式相關聯。

下列範例會在預設「資訊」層級寫入記錄,包括叫用識別碼:

context.log(`Something has happened. Invocation ID: "${context.invocationId}"`);

記錄層級

除了預設 context.log 方法之外,您還可以使用下列方法,讓您在特定層級寫入記錄:

方法 描述
context.log.error() 將錯誤層級事件寫入記錄。
context.log.warn() 將警告層級事件寫入記錄。
context.log.info() 將資訊層級事件寫入記錄。
context.log.verbose() 將追蹤層級事件寫入記錄。
方法 描述
context.trace() 將追蹤層級事件寫入記錄。
context.debug() 將偵錯層級事件寫入記錄。
context.info() 將資訊層級事件寫入記錄。
context.warn() 將警告層級事件寫入記錄。
context.error() 將錯誤層級事件寫入記錄。

設定記錄層級

Azure Functions 可讓您定義追蹤和檢視記錄時要使用的閾值層級。 若要設定閾值,請使用 host.json 檔案中的 logging.logLevel 屬性。 這個屬性可讓您定義在所有函式中套用的預設層級,或每個個別函式的閾值。 若要深入了解,請參閱如何設定 Azure Functions 的監視

追蹤自訂資料

根據預設,Azure Functions 會將輸出寫入為 Application Insights 的追蹤。 如需更多控制項,您可以改用 Application Insights Node.js SDK,將自訂資料傳送至 Application Insights 執行個體。

const appInsights = require("applicationinsights");
appInsights.setup();
const client = appInsights.defaultClient;

module.exports = async function (context, request) {
    // Use this with 'tagOverrides' to correlate custom logs to the parent function invocation.
    var operationIdOverride = {"ai.operation.id":context.traceContext.traceparent};

    client.trackEvent({name: "my custom event", tagOverrides:operationIdOverride, properties: {customProperty2: "custom property value"}});
    client.trackException({exception: new Error("handled exceptions can be logged with this method"), tagOverrides:operationIdOverride});
    client.trackMetric({name: "custom metric", value: 3, tagOverrides:operationIdOverride});
    client.trackTrace({message: "trace message", tagOverrides:operationIdOverride});
    client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL", tagOverrides:operationIdOverride});
    client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true, tagOverrides:operationIdOverride});
};

tagOverrides 參數會將 operation_Id 設為函式的引動過程識別碼。 此設定能夠讓指定的函式叫用過程中所有自動產生和自訂的記錄相互關聯。

HTTP 觸發程序

HTTP 和 Webhook 觸發程序會使用要求和回應物件來呈現 HTTP 訊息。

HTTP 和 Webhook 觸發程序會使用 HttpRequestHttpResponse 物件來呈現 HTTP 訊息。 類別使用 Node.js 的 undici 套件,呈現一部分的擷取標準

HTTP 要求

可以透過數種方式來存取該要求:

  • 作為函式的第二個引數:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${request.url}"`);
    
  • context.req 屬性:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.req.url}"`);
    
  • 從具名輸入繫結:此選項的運作方式與所有非 HTTP 繫結相同。 function.json 中的繫結名稱必須符合 context.bindings 上的索引鍵,或下列範例中的 "request1":

    {
        "name": "request1",
        "type": "httpTrigger",
        "direction": "in",
        "authLevel": "anonymous",
        "methods": [
            "get",
            "post"
        ]
    }
    
    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.bindings.request1.url}"`);
    

HttpRequest 物件具有下列屬性:

屬性 類型​ 描述
method string 用來叫用此函式的 HTTP 要求方法。
url string 要求 URL。
headers Record<string, string> HTTP 要求標頭。 此物件的大小寫有所區別。 建議改用 request.getHeader('header-name'),因為其不區分大小寫。
query Record<string, string> URL 中的查詢字串參數索引鍵和值。
params Record<string, string> 路由參數索引鍵和值。
user HttpRequestUser | null 當沒有這類使用者登入時,代表已登入使用者的物件 (無論是透過 Functions 驗證、SWA 驗證或 Null)。
body Buffer | string | any 如果媒體類型為 "application/octet-stream" 或 "multipart/*",body 為緩衝區。 如果值是 JSON 可剖析的字串,則 body 是經過剖析的物件。 否則,body 會是字串。
rawBody string 作為字串的主體。 儘管有名稱,但此屬性不會傳回緩衝區。
bufferBody Buffer 作為緩衝區的主體。

可以將要求作為 HTTP 觸發函式處理常式的第一個引數來進行存取。

async (request, context) => {
    context.log(`Http function processed request for url "${request.url}"`);

HttpRequest 物件具有下列屬性:

屬性 類型​ 描述
method string 用來叫用此函式的 HTTP 要求方法。
url string 要求 URL。
headers Headers HTTP 要求標頭。
query URLSearchParams URL 中的查詢字串參數索引鍵和值。
params Record<string, string> 路由參數索引鍵和值。
user HttpRequestUser | null 當沒有這類使用者登入時,代表已登入使用者的物件 (無論是透過 Functions 驗證、SWA 驗證或 Null)。
body ReadableStream | null 作為可讀取資料流的主體。
bodyUsed boolean 布爾值,指出本文是否已讀取。

若要存取要求或回應的主體,可以使用下列方法:

方法 傳回類型
arrayBuffer() Promise<ArrayBuffer>
blob() Promise<Blob>
formData() Promise<FormData>
json() Promise<unknown>
text() Promise<string>

注意

主體函式只能執行一次;後續呼叫將會使用空字串/ArrayBuffers 解析。

HTTP 回應

可以透過數種方式來設定此回應:

  • 設定 context.res 屬性:

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • 傳回回應:如果是非同步函式,而且將繫結名稱設為 function.json 中的 $return,您可以直接傳回回應,而不是在 context上進行設定。

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
    module.exports = async function (context, request) {
        return { body: `Hello, world!` };
    
  • 設定具名輸出繫結:此選項的運作方式與所有非 HTTP 繫結相同。 function.json 中的繫結名稱必須符合 context.bindings 上的索引鍵,或下列範例中的 "response1":

    {
        "type": "http",
        "direction": "out",
        "name": "response1"
    }
    
    module.exports = async function (context, request) {
        context.bindings.response1 = { body: `Hello, world!` };
    
  • 呼叫context.res.send()此選項已遭取代。 其會隱含呼叫 context.done(),且無法在非同步函式中使用。

    module.exports = function (context, request) {
        context.res.send(`Hello, world!`);
    

如果您在設定回應時建立新的物件,該物件必須符合具有下列屬性的 HttpResponseSimple 介面:

屬性 類型​ 描述
headers Record<string, string> (選用) HTTP 回應標頭。
cookies Cookie[] (選用) HTTP 回應 cookie。
body any (選用) HTTP 回應主體。
statusCode number (選用) HTTP 回應狀態碼。 如果未設定,則預設為 200
status number (選用) 相當於 statusCode。 如果已設定 statusCode,則會忽略此屬性。

您也可以修改 context.res 物件,而不覆寫物件。 預設 context.res 物件會使用 HttpResponseFull 介面,除了 HttpResponseSimple 屬性之外,還支援下列方法:

方法 描述
status() 設定狀態。
setHeader() 設定標頭欄位。 注意:也支援 res.set()res.header(),並執行相同的操作。
getHeader() 取得標頭欄位。 注意:也支援 res.get(),並執行相同的操作。
removeHeader() 移除標頭。
type() 設定 "content-type" 標頭。
send() 此方法已淘汰。 其會設定主體並呼叫 context.done(),表示同步函式已完成。 注意:也支援 res.end(),並執行相同的操作。
sendStatus() 此方法已淘汰。 其會設定狀態代碼並呼叫 context.done(),表示同步函式已完成。
json() 此方法已淘汰。 其會將 "content-type" 設定為 "application/json"、設定主體,以及呼叫 context.done() 以指出同步函式已完成。

可以透過數種方式來設定此回應:

  • 作為包含類型的簡單介面HttpResponseInit此選項是傳回回應的最簡潔方式。

    return { body: `Hello, world!` };
    

    HttpResponseInit 介面具有下列屬性:

    屬性 類型​ 描述
    body BodyInit (選用) 作為其中一個 ArrayBufferAsyncIterable<Uint8Array>BlobFormDataIterable<Uint8Array>NodeJS.ArrayBufferViewURLSearchParamsnullstring 的 HTTP 回應主體。
    jsonBody any (選用) JSON 可串行化的 HTTP 回應主體。 如果已設定,則會忽略 HttpResponseInit.body 屬性,以支援此屬性。
    status number (選用) HTTP 回應狀態碼。 如果未設定,則預設為 200
    headers HeadersInit (選用) HTTP 回應標頭。
    cookies Cookie[] (選用) HTTP 回應 cookie。
  • 作為具有類型的類別 HttpResponse此選項提供協助程式方法,以讀取和修改回應的各個部分,例如標頭。

    const response = new HttpResponse({ body: `Hello, world!` });
    response.headers.set('content-type', 'application/json');
    return response;
    

    HttpResponse 類別接受選用 HttpResponseInit 作為其建構函式的引數,並具有下列屬性:

    屬性 類型​ 描述
    status number HTTP 回應狀態碼。
    headers Headers HTTP 回應標頭。
    cookies Cookie[] HTTP 回應 cookie。
    body ReadableStream | null 作為可讀取資料流的主體。
    bodyUsed boolean 布林值,指出本主體是否已讀取。

HTTP 資料流 (預覽)

HTTP 數據流是一項功能,可讓您更輕鬆地處理大型數據、串流 OpenAI 回應、提供動態內容,以及支援其他核心 HTTP 案例。 它可讓您將要求串流至 Node.js 函式應用程式中的 HTTP 端點和回應。 在您的應用程式需要透過 HTTP 進行用戶端與伺服器之間的即時交換和互動的情況下,使用 HTTP 串流。 使用 HTTP 時,您也可以使用 HTTP 串流來取得應用程式的最佳效能和可靠性。

HTTP 數據流目前為預覽狀態。

重要

v3 模型中不支援 HTTP 數據流。 升級至 v4 模型 以使用 HTTP 串流功能。

程序設計模型 v4 中的現有 HttpRequestHttpResponse 類型已經支援處理訊息本文的各種方式,包括作為數據流。

必要條件

啟用數據流

使用下列步驟,在 Azure 和本機項目中啟用函式應用程式中的 HTTP 串流:

  1. 如果您打算串流大量數據,請修改 FUNCTIONS_REQUEST_BODY_SIZE_LIMIT Azure 中的設定。 允許的默認主體大小上限是 104857600,這會將您的要求限制為 ~100 MB 的大小。

  2. 針對本機開發,也新增 FUNCTIONS_REQUEST_BODY_SIZE_LIMITlocal.settings.json 檔案

  3. 在主欄位包含的任何檔案中,將下列程式代碼新增至您的應用程式

    const { app } = require('@azure/functions'); 
    
    app.setup({ enableHttpStream: true });
    

資料流範例

此範例顯示 HTTP 觸發的函式,該函式會透過 HTTP POST 要求接收數據,而函式會將此資料串流至指定的輸出檔:

const { app } = require('@azure/functions');
const { createWriteStream } = require('fs');
const { Writable } = require('stream');

app.http('httpTriggerStreamRequest', {
    methods: ['POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const writeStream = createWriteStream('<output file path>');
        await request.body.pipeTo(Writable.toWeb(writeStream));

        return { body: 'Done!' };
    },
});

此範例顯示 HTTP 觸發的函式,此函式會將檔案的內容串流為傳入 HTTP GET 要求的回應:

const { app } = require('@azure/functions');
const { createReadStream } = require('fs');

app.http('httpTriggerStreamResponse', {
    methods: ['GET'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const body = createReadStream('<input file path>');

        return { body };
    },
});

串流考慮

  • 使用 request.body 以取得使用數據流的最大優點。 您仍然可以繼續使用 之類的 request.text()方法,一律會將本文傳回為字串。

勾點

v3 模型中不支援勾點。 升級至 v4 模型以使用勾點。

使用勾點,在 Azure Functions 生命週期中的不同階段執行程式碼。 勾點會依註冊的順序執行,而且可以透過應用程式中的任何檔案加以註冊。 目前有兩個勾點範圍:「應用程式」層級和「叫用」層級。

叫用勾點

preInvocation 勾點之前或 postInvocation 勾點之後,每次叫用函式都會執行一次叫用勾點。 根據預設,勾點會針對所有觸發程序類型執行,但您也可以依類型進行篩選。 下列範例示範如何依觸發程序類型註冊叫用勾點和篩選:

const { app } = require('@azure/functions');

app.hook.preInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `preInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

app.hook.postInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `postInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

勾點處理常式的第一個引數是該勾點類型特定的內容物件。

PreInvocationContext 物件具有下列屬性:

屬性 說明
inputs 傳遞至叫用的引數。
functionHandler 叫用的函式處理常式。 此值的變更會影響函式本身。
invocationContext 傳遞至函式的叫用內容物件。
hookData 在相同範圍中的勾點之間儲存和共用資料的建議位置。 您應該使用唯一的屬性名稱,使其不會與其他勾點的資料發生衝突。

PostInvocationContext 物件具有下列屬性:

屬性 說明
inputs 傳遞至叫用的引數。
result 函式的結果。 此值的變更會影響函式整體結果。
error 函式擲回的錯誤,如果沒有錯誤,則為 null/未定義。 此值的變更會影響函式整體結果。
invocationContext 傳遞至函式的叫用內容物件。
hookData 在相同範圍中的勾點之間儲存和共用資料的建議位置。 您應該使用唯一的屬性名稱,使其不會與其他勾點的資料發生衝突。

應用程式勾點

應用程式勾點會在每個應用程式的執行個體執行一次,無論是在 appStart 勾點中啟動期間,還是在 appTerminate 勾點終止期間。 應用程式終止勾點的執行時間有限,而且不會在任何情節中執行。

Azure Functions 執行階段目前不支援在叫用外部記錄內容。 使用 Application Insights npm 套件,在應用程式層級勾點期間記錄資料。

以下是註冊應用程式勾點的範例:

const { app } = require('@azure/functions');

app.hook.appStart((context) => {
    // add your logic here
});

app.hook.appTerminate((context) => {
    // add your logic here
});

勾點處理常式的第一個引數是該勾點類型特定的內容物件。

AppStartContext 物件具有下列屬性:

屬性 說明
hookData 在相同範圍中的勾點之間儲存和共用資料的建議位置。 您應該使用唯一的屬性名稱,使其不會與其他勾點的資料發生衝突。

AppTerminateContext 物件具有下列屬性:

屬性 說明
hookData 在相同範圍中的勾點之間儲存和共用資料的建議位置。 您應該使用唯一的屬性名稱,使其不會與其他勾點的資料發生衝突。

調整和並行

根據預設,Azure Functions 會自動監視應用程式上的負載,並視需要為 Node.js 建立其他主機執行個體。 Azure Functions 會針對不同的觸發程序類型使用內建 (不是使用者可設定的) 閾值,以決定何時新增執行個體,例如訊息的存留期和 QueueTrigger 的佇列大小。 如需詳細資訊,請參閱耗用量和進階方案的運作方式

這種調整行為足以適用於許多 Node.js 應用程式。 針對 CPU 繫結的應用程式,您可以使用多個語言背景工作處理序進一步改善效能。 您可以使用 FUNCTIONS_WORKER_PROCESS_COUNT 應用程式設定,來增加每個主機的背景工作處理序數目 (從預設值 1 個到最多 10 個)。 Azure Functions 接著會嘗試在這些背景工作中平均散發同時函式叫用。 此行為讓 CPU 密集函式阻止其他函式執行的可能性較低。 此設定適用於 Azure Functions 在擴增應用程式以符合需求時所建立的每個主機。

警告

請小心使用 FUNCTIONS_WORKER_PROCESS_COUNT 設定。 在相同執行個體中執行的多個程序可能會導致無法預測的行為,並延長函式載入時間。 如果您使用此設定,強烈建議從封裝檔案執行來彌補這些缺點。

節點版本

您可以藉由從任何函式記錄 process.version,來查看執行階段目前正在使用的版本。 如需每個程式設計模型支援的 Node.js 版本清單,請參閱 supported versions

設定 Node 版本

您升級 Node.js 版本的方式取決於函數應用程式執行所在的作業系統。

在 Windows 上執行時,會由 WEBSITE_NODE_DEFAULT_VERSION 應用程式設定進行 Node.js 版本的設定。 您可以使用 Azure CLI 或在 Azure 入口網站中更新此設定。

如需 Node.js 版本的詳細資訊,請參閱支援的版本

在升級 Node.js 版本前,請確定函數應用程式是在最新版的 Azure Functions 執行階段上執行。 如需升級執行階段版本,請參閱將應用程式從 Azure Functions 3.x 版移轉至 4.x 版

執行 Azure CLI az functionapp config appsettings set 命令,以更新 Windows 上執行之函數應用程式的 Node.js 版本:

az functionapp config appsettings set  --settings WEBSITE_NODE_DEFAULT_VERSION=~20 \
 --name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME> 

這會將 WEBSITE_NODE_DEFAULT_VERSION 應用程式設定,設為支援的 LTS 版本 ~20

變更之後,函數應用程式會重新啟動。 若要深入了 Node.js 的 Functions 支援,請參閱語言執行階段支援原則

環境變數

環境變數可用於作業祕密 (連接字串、金鑰、端點等) 或分析變數等環境設定。 您可以在本機和雲端環境中新增環境變數,並透過函式程式碼中的 process.env 存取這些變數。

下列範例會記錄 WEBSITE_SITE_NAME 環境變數:

module.exports = async function (context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}
async function timerTrigger1(myTimer, context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}

在本機開發環境中

在本機執行時,函式專案會包含 local.settings.json 檔案,您可以在 Values 物件中儲存環境變數。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "CUSTOM_ENV_VAR_1": "hello",
    "CUSTOM_ENV_VAR_2": "world"
  }
}

在 Azure 雲端環境中

在 Azure 中執行時,函數應用程式可讓您設定和使用應用程式設定,例如服務連接字串,並在執行期間將這些設定公開為環境變數。

有數種方式可供您新增、更新和刪除函式應用程式設定:

對函式應用程式設定的變更需要將函式應用程式重新啟動。

背景工作角色環境變數

存在數個 Node.js 專屬的 Functions 環境變數:

languageWorkers__node__arguments

此設定可讓您在啟動 Node.js 程序時指定自訂引數。 在本機最常使用此設定,以在偵錯模式中啟動背景工作角色,但如果您需要自訂引數,也可以在 Azure 中使用此設定。

警告

可能的話,請避免在 Azure 中使用 languageWorkers__node__arguments,因為其可能會對冷啟動時間產生負面影響。 執行階段必須使用自訂引數,從頭開始啟動新的背景工作角色,而不是使用預先準備的背景工作角色。

logging__logLevel__Worker

此設定會調整 Node.js 特定背景工作角色記錄的預設記錄層級。 根據預設,只會顯示警告或錯誤記錄檔,但您可以將其設定為 informationdebug,以協助診斷 Node.js 背景工作角色的相關問題。 如需詳細資訊,請參閱設定記錄層級

ECMAScript 模組 (預覽)

注意

因為 ECMAScript 模組目前是 Azure Functions 中 Node.js 14 或更高版本的預覽功能。

ECMAScript 模組 (ES 模組) 是 Node.js 新的官方標準模組系統。 到目前為止,本文中的程式碼範例會使用 CommonJS 語法。 在 Node.js 14 或更新版本中執行 Azure Functions 時,您可以選擇使用 ES 模組語法撰寫函式。

若要在函式中使用 ES 模組,請將其檔案名稱變更為使用 .mjs 延伸模組。 下列 index.mjs 檔案範例是使用 ES 模組語法匯入 uuid 程式庫並傳回值的 HTTP 觸發函式。

import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(context, request) {
    context.res.body = uuidv4();
};

export default httpTrigger;
import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(request, context) {
    return { body: uuidv4() };
};

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    handler: httpTrigger1
});

設定函式進入點

function.json 屬性 scriptFileentryPoint 可用來對於匯出的函式設定位置和名稱。 如果您使用的是 TypeScript,則必須使用 scriptFile 屬性,其應指向已編譯的 JavaScript。

使用 scriptFile

預設會從 index.js 執行 JavaScript 函式,這是與對應的 function.json 共用相同父目錄的檔案。

scriptFile 可用來取得如下列範例所示的資料夾結構:

<project_root>/
 | - node_modules/
 | - myFirstFunction/
 | | - function.json
 | - lib/
 | | - sayHello.js
 | - host.json
 | - package.json

myFirstFunctionfunction.json 應該包含 scriptFile 屬性,這個屬性指向有匯出的函式要執行的檔案。

{
  "scriptFile": "../lib/sayHello.js",
  "bindings": [
    ...
  ]
}

使用 entryPoint

在 v3 模型中,必須使用 module.exports 匯出函式,才能找到並執行該函式。 觸發時執行的函式預設是僅來自該檔案的匯出,名為 run 的匯出,或名為 index 的匯出。 下列範例會將 function.json 中的 entryPoint 設定為自訂值 "logHello":

{
  "entryPoint": "logHello",
  "bindings": [
    ...
  ]
}
async function logHello(context) {
    context.log('Hello, world!');
}

module.exports = { logHello };

本機偵錯

建議您使用 VS Code 進行本機偵錯,這會在偵錯模式中自動啟動 Node.js 程序,並將其連結至程序。 如需詳細資訊,請參閱在本機執行函式

如果您使用不同的工具來偵錯,或想要以手動方式在偵錯模式中啟動 Node.js 程序,請在 local.settings.jsonValues 底下新增 "languageWorkers__node__arguments": "--inspect"。 根據預設,--inspect 引數會告訴 Node.js 在連接埠 9229 上接聽偵錯用戶端。 如需詳細資訊,請參閱 Node.js 偵錯指南

建議

本節說明建議遵循之 Node.js 應用程式的數個具影響力模式。

選擇單一 vCPU App Service 方案

當您建立使用 App Service 方案的函數應用程式時,建議您選取單一 vCPU 方案,而非具有多個 vCPU 的方案。 目前 Functions 在單一 vCPU VM 上執行 Node.js 函式會較有效率,而使用較大的 VM 並不會產生預期的效能改進。 如有必要,您可以新增更多單一 vCPU VM 執行個體來手動進行擴增,或者您可以啟用自動規模調整。 如需詳細資訊,請參閱手動或自動調整執行個體計數規模

從封裝檔案執行

在無伺服器裝載模型中開發 Azure Functions 時,可進行冷啟動。 冷啟動是指函數應用程式在閒置一段時間之後進行的第一次啟動,這需要較長的時間啟動。 尤其是對於大型相依性樹狀結構的 Node.js 應用程式,冷啟動可能會有很大的影響。 若要加速執行冷啟動程序,請盡可能以套件檔案的形式執行函式。 根據預設,許多部署方法都會使用此模型,但如果遇到大型冷啟動,則應該檢查以確保您的執行方式。

使用單一靜態用戶端

當您在 Azure Functions 應用程式中使用服務特定的用戶端時,請勿在每次函式叫用時新建用戶端,因為可能會達到連線限制。 相反地,請在全域範圍中建立單一靜態用戶端。 如需詳細資訊,請參閱管理 Azure Functions 中的連線

使用 asyncawait

在 Node.js 中撰寫 Azure Functions 時,您應該使用 asyncawait 關鍵字撰寫程式碼。 使用 asyncawait 撰寫程式碼,而不是使用 Promises 回呼或 .then.catch 來協助避免兩個常見問題:

  • 擲回損毀 Node.js 程序的未攔截例外狀況,可能會影響其他函式的執行。
  • 非預期的行為,例如 context.log 中遺漏記錄,是由未正確等候的非同步呼叫所造成。

在下列範例中,會以錯誤優先的回呼函式作為其第二個參數來叫用非同步方法 fs.readFile。 此程式碼會造成上述兩個問題。 在正確範圍中未明確攔截到的例外狀況可能會導致整個程序損毀 (問題 #1)。 傳回而未確保回呼完成,意味著 HTTP 回應的主體有時候會是空的 (問題 #2)。

// DO NOT USE THIS CODE
const { app } = require('@azure/functions');
const fs = require('fs');

app.http('httpTriggerBadAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        let fileData;
        fs.readFile('./helloWorld.txt', (err, data) => {
            if (err) {
                context.error(err);
                // BUG #1: This will result in an uncaught exception that crashes the entire process
                throw err;
            }
            fileData = data;
        });
        // BUG #2: fileData is not guaranteed to be set before the invocation ends
        return { body: fileData };
    },
});

在下列範例中,會以錯誤優先的回呼函式作為其第二個參數來叫用非同步方法 fs.readFile。 此程式碼會造成上述兩個問題。 在正確範圍中未明確攔截到的例外狀況可能會導致整個程序損毀 (問題 #1)。 在回呼範圍之外呼叫已遭取代的 context.done() 方法,可能會在讀取檔案之前發出函式已完成的訊號 (問題 #2)。 在此範例中,太早呼叫 context.done() 會導致從 Data from file: 開始遺漏記錄項目。

// NOT RECOMMENDED PATTERN
const fs = require('fs');

module.exports = function (context) {
    fs.readFile('./hello.txt', (err, data) => {
        if (err) {
            context.log.error('ERROR', err);
            // BUG #1: This will result in an uncaught exception that crashes the entire process
            throw err;
        }
        context.log(`Data from file: ${data}`);
        // context.done() should be called here
    });
    // BUG #2: Data is not guaranteed to be read before the Azure Function's invocation ends
    context.done();
}

使用 asyncawait 關鍵字可協助避免這兩個錯誤。 Node.js 生態系統中的大多數 API 已轉換為支援某種形式的承諾。 例如,從 v14 開始,Node.js 會提供 fs/promises API 來取代 fs 回呼 API。

在下列範例中,在函式執行期間擲回的任何未處理的例外狀況,只會導致引發例外狀況的個別叫用失敗。 await 關鍵字表示只有在完成之後,才會執行 readFile 的後續步驟。

// Recommended pattern
const { app } = require('@azure/functions');
const fs = require('fs/promises');

app.http('httpTriggerGoodAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        try {
            const fileData = await fs.readFile('./helloWorld.txt');
            return { body: fileData };
        } catch (err) {
            context.error(err);
            // This rethrown exception will only fail the individual invocation, instead of crashing the whole process
            throw err;
        }
    },
});

使用 asyncawait,您也不需要呼叫 context.done() 回呼。

// Recommended pattern
const fs = require('fs/promises');

module.exports = async function (context) {
    let data;
    try {
        data = await fs.readFile('./hello.txt');
    } catch (err) {
        context.log.error('ERROR', err);
        // This rethrown exception will be handled by the Functions Runtime and will only fail the individual invocation
        throw err;
    }
    context.log(`Data from file: ${data}`);
}

疑難排解

請參閱 Node.js 疑難排解指南

下一步

如需詳細資訊,請參閱以下資源: