Guida per sviluppatori di Funzioni di Azure Node.js

Questa guida è un'introduzione allo sviluppo di Funzioni di Azure usando JavaScript o TypeScript. L'articolo presuppone che l'utente abbia già letto la guida per sviluppatori Funzioni di Azure.

Importante

Il contenuto di questo articolo cambia in base alla scelta del modello di programmazione Node.js nel selettore nella parte superiore di questa pagina. La versione scelta deve corrispondere alla versione del @azure/functions pacchetto npm in uso nell'app. Se il pacchetto non è elencato in package.json, il valore predefinito è v3. Altre informazioni sulle differenze tra v3 e v4 sono disponibili nella guida alla migrazione.

Gli sviluppatori di Node.js potrebbero essere interessati anche a uno degli articoli seguenti:

Introduzione Concetti Formazione guidata

Considerazioni

  • Il modello di programmazione Node.js non deve essere confuso con il runtime di Funzioni di Azure:
    • Modello di programmazione: definisce come creare il codice ed è specifico di JavaScript e TypeScript.
    • Runtime: definisce il comportamento sottostante di Funzioni di Azure e viene condiviso in tutti i linguaggi.
  • La versione del modello di programmazione è strettamente associata alla versione del @azure/functions pacchetto npm. Il controllo delle versioni viene eseguito indipendentemente dal runtime. Sia il runtime che il modello di programmazione usano il numero 4 come versione principale più recente, ma questa è una coincidenza.
  • Non è possibile combinare i modelli di programmazione v3 e v4 nella stessa app per le funzioni. Non appena si registra una funzione v4 nell'app, tutte le funzioni v3 registrate nei file function.json vengono ignorate.

Versioni supportate

La tabella seguente illustra ogni versione del modello di programmazione Node.js insieme alle versioni supportate del runtime di Funzioni di Azure e Node.js.

Versione del modello di programmazione Livello di supporto Versione del runtime di Funzioni versione Node.js Descrizione
4.x Disponibilità generale 4.25+ 20.x, 18.x Supporta una struttura di file flessibile e un approccio incentrato sul codice per trigger e associazioni.
3.x Disponibilità generale 4.x 20.x, 18.x, 16.x, 14.x Richiede una struttura di file specifica con i trigger e le associazioni dichiarati in un file "function.json"
2.x n/d 3.x 14.x, 12.x, 10.x È stata raggiunta la fine del supporto il 13 dicembre 2022. Per altre informazioni, vedi Versioni di Funzioni.
1.x n/d 2.x 10.x, 8.x È stata raggiunta la fine del supporto il 13 dicembre 2022. Per altre informazioni, vedi Versioni di Funzioni.

Struttura delle cartelle

La struttura di cartelle richiesta per un progetto JavaScript è simile all'esempio seguente:

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

La cartella principale del progetto, <project_root>, può contenere i file seguenti:

  • .vscode/: (facoltativo) Contiene la configurazione di Visual Studio Code archiviata. Per altre informazioni, vedere Impostazioni di Visual Studio Code.
  • myFirstFunction/function.json: contiene la configurazione per il trigger, gli input e gli output della funzione. Il nome della directory determina il nome della funzione.
  • myFirstFunction/index.js: archivia il codice della funzione. Per modificare questo percorso di file predefinito, vedere uso di scriptFile.
  • .funcignore: (facoltativo) Dichiara i file che non devono essere pubblicati in Azure. In genere, questo file contiene vscode/ per ignorare l'impostazione dell'editor, il test/ per ignorare i test case e local.settings.json per impedire la pubblicazione delle impostazioni dell'app locale.
  • host.json: contiene opzioni di configurazione che influiscono su tutte le funzioni in un'istanza dell'app per le funzioni. Questo file viene pubblicato in Azure. Non tutte le opzioni sono supportate durante l'esecuzione in locale. Per altre informazioni, vedere host.json.
  • local.settings.json: usato per archiviare le impostazioni e i stringa di connessione dell'app quando è in esecuzione in locale. Questo file non viene pubblicato in Azure. Per altre informazioni, vedere local.settings.file.
  • package.json: contiene opzioni di configurazione come un elenco di dipendenze dei pacchetti, il punto di ingresso principale e gli script.

La struttura di cartelle consigliata per un progetto JavaScript è simile all'esempio seguente:

<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

La cartella principale del progetto, <project_root>, può contenere i file seguenti:

  • .vscode/: (facoltativo) Contiene la configurazione di Visual Studio Code archiviata. Per altre informazioni, vedere Impostazioni di Visual Studio Code.
  • src/functions/: percorso predefinito per tutte le funzioni e i relativi trigger e associazioni correlati.
  • test/: (facoltativo) Contiene i test case dell'app per le funzioni.
  • .funcignore: (facoltativo) Dichiara i file che non devono essere pubblicati in Azure. In genere, questo file contiene vscode/ per ignorare l'impostazione dell'editor, il test/ per ignorare i test case e local.settings.json per impedire la pubblicazione delle impostazioni dell'app locale.
  • host.json: contiene opzioni di configurazione che influiscono su tutte le funzioni in un'istanza dell'app per le funzioni. Questo file viene pubblicato in Azure. Non tutte le opzioni sono supportate durante l'esecuzione in locale. Per altre informazioni, vedere host.json.
  • local.settings.json: usato per archiviare le impostazioni e i stringa di connessione dell'app quando è in esecuzione in locale. Questo file non viene pubblicato in Azure. Per altre informazioni, vedere local.settings.file.
  • package.json: contiene opzioni di configurazione come un elenco di dipendenze dei pacchetti, il punto di ingresso principale e gli script.

Registrazione di una funzione

Il modello v3 registra una funzione in base all'esistenza di due file. Prima di tutto, è necessario un function.json file che si trova in una cartella di un livello inferiore dalla radice dell'app. In secondo luogo, è necessario un file JavaScript che esporta la funzione. Per impostazione predefinita, il modello cerca un index.js file nella stessa cartella di function.json. Se si usa TypeScript, è necessario usare la scriptFile proprietà in function.json per puntare al file JavaScript compilato. Per personalizzare il percorso del file o il nome di esportazione della funzione, vedere Configurazione del punto di ingresso della funzione.

La funzione esportata deve essere sempre dichiarata come nel async function modello v3. È possibile esportare una funzione sincrona, ma è necessario chiamare context.done() per segnalare che la funzione è stata completata, che è deprecata e non consigliata.

La funzione viene passata come context primo argomento e gli input come argomenti rimanenti.

L'esempio seguente è una funzione semplice che registra che è stata attivata e risponde con 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!' };
};

Il modello di programmazione carica le funzioni in base al campo dell'oggetto mainpackage.json. È possibile impostare il main campo su un singolo file o più file usando un modello GLOB. La tabella seguente mostra i valori di esempio per il main campo:

Esempio Descrizione
src/index.js Registrare le funzioni da un singolo file radice.
src/functions/*.js Registrare ogni funzione dal proprio file.
src/{index.js,functions/*.js} Combinazione in cui si registra ogni funzione dal proprio file, ma è ancora disponibile un file radice per il codice generale a livello di app.

Per registrare una funzione, è necessario importare l'oggetto app dal @azure/functions modulo npm e chiamare il metodo specifico per il tipo di trigger. Il primo argomento durante la registrazione di una funzione è il nome della funzione. Il secondo argomento è un options oggetto che specifica la configurazione per il trigger, il gestore e qualsiasi altro input o output. In alcuni casi in cui la configurazione del trigger non è necessaria, è possibile passare il gestore direttamente come secondo argomento anziché come options oggetto .

La registrazione di una funzione può essere eseguita da qualsiasi file nel progetto, purché tale file venga caricato (direttamente o indirettamente) in base al main campo nel package.json file. La funzione deve essere registrata in un ambito globale perché non è possibile registrare le funzioni dopo l'avvio delle esecuzioni.

L'esempio seguente è una funzione semplice che registra che è stata attivata e risponde con 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!' };
    }
});

Input e output

La funzione è necessaria per avere esattamente un input primario denominato trigger. Può anche avere input e/o output secondari. Gli input e gli output sono configurati nei function.json file e vengono definiti anche associazioni.

Input

Gli input sono associazioni con direction impostato su in. La differenza principale tra un trigger e un input secondario consiste nel fatto che per type un trigger termina in Trigger, ad esempio tipo vs tipo blobTriggerblob. La maggior parte delle funzioni usa solo un trigger e non molti tipi di input secondari sono supportati.

È possibile accedere agli input in diversi modi:

  • [Scelta consigliata] Come argomenti passati alla funzione: usare gli argomenti nello stesso ordine in cui sono definiti in function.json. La name proprietà definita in function.json non deve corrispondere al nome dell'argomento, anche se è consigliata per l'organizzazione.

    module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
    
  • Come proprietà di context.bindings: usare la chiave corrispondente alla name proprietà definita in function.json.

    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);
    };
    

Output

Gli output sono binding con direction impostato su out e possono essere impostati in diversi modi:

  • [Consigliato per un singolo output] Restituisce direttamente il valore: se si usa una funzione asincrona, è possibile restituire direttamente il valore. È necessario modificare la name proprietà dell'associazione di output in $return in come function.json nell'esempio seguente:

    {
        "name": "$return",
        "type": "http",
        "direction": "out"
    }
    
    module.exports = async function (context, request) {
        return {
            body: "Hello, world!"
        };
    }
    
  • [Consigliato per più output] Restituisce un oggetto contenente tutti gli output: se si usa una funzione asincrona, è possibile restituire un oggetto con una proprietà corrispondente al nome di ogni associazione nell'oggetto function.json. L'esempio seguente usa associazioni di output denominate "httpResponse" e "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
        };
    };
    
  • Impostare i valori in context.bindings: se non si usa una funzione asincrona o non si vogliono usare le opzioni precedenti, è possibile impostare i valori direttamente su context.bindings, dove la chiave corrisponde al nome dell'associazione. L'esempio seguente usa associazioni di output denominate "httpResponse" e "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;
    };
    

Tipo di dati delle associazioni

È possibile usare la dataType proprietà in un'associazione di input per modificare il tipo di input, ma presenta alcune limitazioni:

  • In Node.js solo string e binary sono supportati (stream non è)
  • Per gli input HTTP, la dataType proprietà viene ignorata. Usare invece le proprietà sull'oggetto request per ottenere il corpo nel formato desiderato. Per altre informazioni, vedere Richiesta HTTP.

Nell'esempio seguente di un trigger della coda di archiviazione, il tipo predefinito di myQueueItem è , stringma se si imposta su dataTypebinary, il tipo passa a un 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');
    }
};

La funzione è necessaria per avere esattamente un input primario denominato trigger. Può anche avere input secondari, un output primario denominato output restituito e/o output secondari. Gli input e gli output vengono definiti anche associazioni esterne al contesto del modello di programmazione Node.js. Prima della versione 4 del modello, queste associazioni sono state configurate nei function.json file.

Input del trigger

Il trigger è l'unico input o output necessario. Per la maggior parte dei tipi di trigger, si registra una funzione usando un metodo sull'oggetto app denominato dopo il tipo di trigger. È possibile specificare la configurazione specifica del trigger direttamente nell'argomento options . Ad esempio, un trigger HTTP consente di specificare una route. Durante l'esecuzione, il valore corrispondente a questo trigger viene passato come primo argomento al gestore.

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

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

Restituire l'output

L'output restituito è facoltativo e in alcuni casi configurato per impostazione predefinita. Ad esempio, un trigger HTTP registrato con app.http è configurato per restituire automaticamente un output di risposta HTTP. Per la maggior parte dei tipi di output, specificare la configurazione restituita nell'argomento options con l'aiuto dell'oggetto output esportato dal @azure/functions modulo. Durante l'esecuzione, l'output viene impostato restituendolo dal gestore.

L'esempio seguente usa un trigger timer e un output della coda di archiviazione:

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

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

Input e output aggiuntivi

Oltre al trigger e alla restituzione, è possibile specificare input o output aggiuntivi sull'argomento durante la options registrazione di una funzione. Gli input oggetti e output esportati dal @azure/functions modulo forniscono metodi specifici del tipo per creare la configurazione. Durante l'esecuzione, si ottengono o impostano i valori con context.extraInputs.get o context.extraOutputs.set, passando l'oggetto di configurazione originale come primo argomento.

L'esempio seguente è una funzione attivata da una coda di archiviazione, con un input BLOB di archiviazione aggiuntivo copiato in un output DEL BLOB di archiviazione aggiuntivo. Il messaggio della coda deve essere il nome di un file e sostituisce {queueTrigger} come nome del BLOB da copiare, con l'aiuto di un'espressione di associazione.

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);
    }
});

Input e output generici

Gli appoggetti , trigger, inpute output esportati dal modulo forniscono metodi specifici del tipo per la @azure/functions maggior parte dei tipi. Per tutti i tipi non supportati, viene fornito un generic metodo per consentire di specificare manualmente la configurazione. Il generic metodo può essere usato anche se si desidera modificare le impostazioni predefinite fornite da un metodo specifico del tipo.

L'esempio seguente è una semplice funzione attivata tramite HTTP usando metodi generici anziché metodi specifici del tipo.

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!` };
    }
});

Contesto di chiamata

Ogni chiamata della funzione viene passata a un oggetto chiamata context , usato per leggere gli input, impostare output, scrivere nei log e leggere vari metadati. Nel modello v3 l'oggetto contesto è sempre il primo argomento passato al gestore.

Di seguito sono elencate le proprietà dell'oggetto context:

Proprietà Descrizione
invocationId ID della chiamata alla funzione corrente.
executionContext Vedere contesto di esecuzione.
bindings Vedere binding.
bindingData Metadati relativi all'input del trigger per questa chiamata, non incluso il valore stesso. Ad esempio, un trigger dell'hub eventi ha una enqueuedTimeUtc proprietà .
traceContext Contesto per la traccia distribuita. Per ulteriori informazioni, vedere Trace Context.
bindingDefinitions Configurazione degli input e degli output, come definito in function.json.
req Vedere Richiesta HTTP.
res Vedere Risposta HTTP.

context.executionContext

Di seguito sono elencate le proprietà dell'oggetto context.executionContext:

Proprietà Descrizione
invocationId ID della chiamata alla funzione corrente.
functionName Nome della funzione richiamata. Il nome della cartella contenente il function.json file determina il nome della funzione.
functionDirectory Cartella contenente il function.json file.
retryContext Vedere contesto di ripetizione dei tentativi.

context.executionContext.retryContext

Di seguito sono elencate le proprietà dell'oggetto context.executionContext.retryContext:

Proprietà Descrizione
retryCount Numero che rappresenta il tentativo di ripetizione corrente.
maxRetryCount Numero massimo di tentativi di esecuzione. Valore di -1 mezzi per riprovare per un periodo illimitato.
exception Eccezione che ha causato il nuovo tentativo.

context.bindings

L'oggetto context.bindings viene usato per leggere gli input o impostare output. L'esempio seguente è un trigger della coda di archiviazione, che usa context.bindings per copiare un input del BLOB di archiviazione in un output del BLOB di archiviazione. Il contenuto del messaggio della coda sostituisce {queueTrigger} come nome file da copiare, con l'aiuto di un'espressione di associazione.

{
    "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

Il context.done metodo è deprecato. Prima che le funzioni asincrone siano supportate, è necessario segnalare che la funzione viene eseguita chiamando context.done():

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

A questo punto, è consigliabile rimuovere la chiamata a context.done() e contrassegnare la funzione come asincrona in modo che restituisca una promessa (anche se non await si ha nulla). Al termine della funzione (in altre parole, la promessa restituita viene risolta), il modello v3 sa che viene eseguita la funzione.

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

Ogni chiamata della funzione viene passata a un oggetto di context chiamata, con informazioni sulla chiamata e sui metodi usati per la registrazione. Nel modello v4 l'oggetto context è in genere il secondo argomento passato al gestore.

La InvocationContext classe ha le proprietà seguenti:

Proprietà Descrizione
invocationId ID della chiamata alla funzione corrente.
functionName Nome della funzione.
extraInputs Usato per ottenere i valori di input aggiuntivi. Per altre informazioni, vedere Input e output aggiuntivi.
extraOutputs Utilizzato per impostare i valori di output aggiuntivi. Per altre informazioni, vedere Input e output aggiuntivi.
retryContext Vedere contesto di ripetizione dei tentativi.
traceContext Contesto per la traccia distribuita. Per ulteriori informazioni, vedere Trace Context.
triggerMetadata Metadati relativi all'input del trigger per questa chiamata, non incluso il valore stesso. Ad esempio, un trigger dell'hub eventi ha una enqueuedTimeUtc proprietà .
options Le opzioni usate durante la registrazione della funzione, dopo che sono state convalidate e con le impostazioni predefinite specificate in modo esplicito.

Contesto di ripetizione dei tentativi

Di seguito sono elencate le proprietà dell'oggetto retryContext:

Proprietà Descrizione
retryCount Numero che rappresenta il tentativo di ripetizione corrente.
maxRetryCount Numero massimo di tentativi di esecuzione. Valore di -1 mezzi per riprovare per un periodo illimitato.
exception Eccezione che ha causato il nuovo tentativo.

Per ulteriori informazioni, vedere retry-policies.

Registrazione

In Funzioni di Azure è consigliabile usare context.log() per scrivere i log. Funzioni di Azure si integra con app Azure lication Insights per acquisire meglio i log dell'app per le funzioni. Application Insights, parte di Monitoraggio di Azure, offre funzionalità per la raccolta, il rendering visivo e l'analisi dei log applicazioni e degli output di traccia. Per altre informazioni, vedere Monitoraggio Funzioni di Azure.

Nota

Se si usa il metodo di Node.js console.log alternativo, questi log vengono rilevati a livello di app e non verranno associati a alcuna funzione specifica. È consigliabile usare context per la registrazione invece di console in modo che tutti i log siano associati a una funzione specifica.

Nell'esempio seguente viene scritto un log a livello di "informazioni" predefinito, incluso l'ID chiamata:

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

Livelli di registrazione

Oltre al metodo predefinito context.log , sono disponibili i metodi seguenti che consentono di scrivere log a livelli specifici:

metodo Descrizione
context.log.error() Scrive un evento a livello di errore nei log.
context.log.warn() Scrive un evento a livello di avviso nei log.
context.log.info() Scrive un evento a livello di informazioni nei log.
context.log.verbose() Scrive un evento a livello di traccia nei log.
metodo Descrizione
context.trace() Scrive un evento a livello di traccia nei log.
context.debug() Scrive un evento a livello di debug nei log.
context.info() Scrive un evento a livello di informazioni nei log.
context.warn() Scrive un evento a livello di avviso nei log.
context.error() Scrive un evento a livello di errore nei log.

Configurare il livello di log

Funzioni di Azure consente di definire il livello di soglia da usare durante il rilevamento e la visualizzazione dei log. Per impostare la soglia, utilizzare la logging.logLevel proprietà nel host.json file . Questa proprietà consente di definire un livello predefinito applicato a tutte le funzioni o una soglia per ogni singola funzione. Per altre informazioni, vedere Come configurare il monitoraggio per Funzioni di Azure.

Tenere traccia dei dati personalizzati

Per impostazione predefinita, Funzioni di Azure scrive l'output come tracce in Application Insights. Per un maggiore controllo, è invece possibile usare Application Insights Node.js SDK per inviare dati personalizzati all'istanza di 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});
};

Il parametro tagOverrides imposta operation_Id sull'ID di chiamata alla funzione. Questa impostazione consente di correlare tutti i log generati e personalizzati automaticamente per una determinata chiamata di funzione.

Trigger HTTP

I trigger HTTP e webhook usano oggetti richiesta e risposta per rappresentare i messaggi HTTP.

I trigger HTTP e webhook usano HttpRequest oggetti e HttpResponse per rappresentare i messaggi HTTP. Le classi rappresentano un subset dello standard di recupero, usando il pacchetto di undici Node.js.

Richiesta HTTP

È possibile accedere alla richiesta in diversi modi:

  • Come secondo argomento della funzione:

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

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.req.url}"`);
    
  • Dalle associazioni di input denominate: questa opzione funziona come qualsiasi associazione non HTTP. Il nome dell'associazione in function.json deve corrispondere alla chiave in context.bindingso "request1" nell'esempio seguente:

    {
        "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}"`);
    

Di seguito sono elencate le proprietà dell'oggetto HttpRequest:

Proprietà Type Descrizione
method string Metodo di richiesta HTTP usato per richiamare questa funzione.
url string URL della richiesta.
headers Record<string, string> Intestazioni di richiesta HTTP. Questo oggetto fa distinzione tra maiuscole e minuscole. È consigliabile usare request.getHeader('header-name') invece , senza distinzione tra maiuscole e minuscole.
query Record<string, string> Chiavi e valori dei parametri della stringa di query dall'URL.
params Record<string, string> Indirizzare chiavi e valori dei parametri.
user HttpRequestUser | null Oggetto che rappresenta l'utente connesso, tramite l'autenticazione di Funzioni, l'autenticazione SWA o null quando tale utente non è connesso.
body Buffer | string | any Se il tipo di supporto è "application/octet-stream" o "multipart/*", body è un buffer. Se il valore è una stringa in grado di analizzare JSON, body è l'oggetto analizzato. In caso contrario, body è una stringa.
rawBody string Corpo come stringa. Nonostante il nome, questa proprietà non restituisce un buffer.
bufferBody Buffer Corpo come buffer.

È possibile accedere alla richiesta come primo argomento al gestore per una funzione attivata da HTTP.

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

Di seguito sono elencate le proprietà dell'oggetto HttpRequest:

Proprietà Type Descrizione
method string Metodo di richiesta HTTP usato per richiamare questa funzione.
url string URL della richiesta.
headers Headers Intestazioni di richiesta HTTP.
query URLSearchParams Chiavi e valori dei parametri della stringa di query dall'URL.
params Record<string, string> Indirizzare chiavi e valori dei parametri.
user HttpRequestUser | null Oggetto che rappresenta l'utente connesso, tramite l'autenticazione di Funzioni, l'autenticazione SWA o null quando tale utente non è connesso.
body ReadableStream | null Corpo come flusso leggibile.
bodyUsed boolean Valore booleano che indica se il corpo è già letto.

Per accedere al corpo di una richiesta o di una risposta, è possibile usare i metodi seguenti:

metodo Tipo restituito
arrayBuffer() Promise<ArrayBuffer>
blob() Promise<Blob>
formData() Promise<FormData>
json() Promise<unknown>
text() Promise<string>

Nota

Le funzioni del corpo possono essere eseguite una sola volta; le chiamate successive verranno risolte con stringhe vuote/ArrayBuffers.

Risposta HTTP

La risposta può essere impostata in diversi modi:

  • Impostare la context.res proprietà :

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • Restituisce la risposta: se la funzione è asincrona e si imposta il nome dell'associazione su $return in function.json, è possibile restituire direttamente la risposta anziché impostarla su context.

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
    module.exports = async function (context, request) {
        return { body: `Hello, world!` };
    
  • Impostare l'associazione di output denominata: questa opzione funziona come qualsiasi associazione non HTTP. Il nome dell'associazione in function.json deve corrispondere alla chiave in context.bindingso "response1" nell'esempio seguente:

    {
        "type": "http",
        "direction": "out",
        "name": "response1"
    }
    
    module.exports = async function (context, request) {
        context.bindings.response1 = { body: `Hello, world!` };
    
  • Chiamata context.res.send(): questa opzione è deprecata. Chiama in modo context.done() implicito e non può essere usato in una funzione asincrona.

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

Se si crea un nuovo oggetto quando si imposta la risposta, tale oggetto deve corrispondere all'interfaccia HttpResponseSimple , che ha le proprietà seguenti:

Proprietà Type Descrizione
headers Record<string, string> (facoltativo) Intestazioni di risposta HTTP.
cookies Cookie[] (facoltativo) Cookie di risposta HTTP.
body any (facoltativo) Corpo della risposta HTTP.
statusCode number (facoltativo) Codice di stato della risposta HTTP. Se non è impostato, il valore predefinito è 200.
status number (facoltativo) Uguale a statusCode. Questa proprietà viene ignorata se statusCode è impostata.

È anche possibile modificare l'oggetto context.res senza sovrascriverlo. L'oggetto predefinito context.res usa l'interfaccia HttpResponseFull , che supporta i metodi seguenti oltre alle HttpResponseSimple proprietà :

metodo Descrizione
status() Imposta lo stato.
setHeader() Imposta un campo di intestazione. NOTA: res.set() e res.header() sono supportati e fanno la stessa cosa.
getHeader() Ottenere un campo di intestazione. NOTA: res.get() è supportato e fa la stessa cosa.
removeHeader() Rimuove un'intestazione.
type() Imposta l'intestazione "content-type".
send() Questo metodo è deprecato. Imposta il corpo e le chiamate context.done() per indicare che una funzione di sincronizzazione è stata completata. NOTA: res.end() è supportato e fa la stessa cosa.
sendStatus() Questo metodo è deprecato. Imposta il codice di stato e le chiamate context.done() per indicare che una funzione di sincronizzazione è stata completata.
json() Questo metodo è deprecato. Imposta "content-type" su "application/json", imposta il corpo e chiama context.done() per indicare che una funzione di sincronizzazione è stata completata.

La risposta può essere impostata in diversi modi:

  • Come interfaccia semplice con tipo HttpResponseInit: questa opzione è il modo più conciso di restituire risposte.

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

    L'interfaccia HttpResponseInit ha le proprietà seguenti:

    Proprietà Type Descrizione
    body BodyInit (facoltativo) Corpo della risposta HTTP come uno di ArrayBuffer, BlobAsyncIterable<Uint8Array>, FormData, , NodeJS.ArrayBufferViewIterable<Uint8Array>, URLSearchParams, null, o string.
    jsonBody any (facoltativo) Corpo della risposta HTTP serializzabile in JSON. Se impostata, la HttpResponseInit.body proprietà viene ignorata a favore di questa proprietà.
    status number (facoltativo) Codice di stato della risposta HTTP. Se non è impostato, il valore predefinito è 200.
    headers HeadersInit (facoltativo) Intestazioni di risposta HTTP.
    cookies Cookie[] (facoltativo) Cookie di risposta HTTP.
  • Come classe con tipo HttpResponse: questa opzione fornisce metodi helper per la lettura e la modifica di varie parti della risposta, ad esempio le intestazioni.

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

    La HttpResponse classe accetta un argomento facoltativo HttpResponseInit come argomento per il relativo costruttore e ha le proprietà seguenti:

    Proprietà Type Descrizione
    status number Codice di stato della risposta HTTP.
    headers Headers Intestazioni di risposta HTTP.
    cookies Cookie[] Cookie di risposta HTTP.
    body ReadableStream | null Corpo come flusso leggibile.
    bodyUsed boolean Valore booleano che indica se il corpo è già stato letto.

Flussi HTTP (anteprima)

I flussi HTTP sono una funzionalità che semplifica l'elaborazione di dati di grandi dimensioni, lo streaming di risposte OpenAI, la distribuzione di contenuto dinamico e il supporto di altri scenari HTTP principali. Consente di trasmettere le richieste e le risposte dagli endpoint HTTP nell'app per le funzioni Node.js. Usare i flussi HTTP negli scenari in cui l'app richiede lo scambio in tempo reale e l'interazione tra client e server tramite HTTP. È anche possibile usare flussi HTTP per ottenere prestazioni e affidabilità ottimali per le app quando si usa HTTP.

I flussi HTTP sono attualmente in anteprima.

Importante

I flussi HTTP non sono supportati nel modello v3. Eseguire l'aggiornamento al modello v4 per usare la funzionalità di streaming HTTP.

I tipi e HttpResponse esistenti HttpRequest nel modello di programmazione v4 supportano già vari modi di gestire il corpo del messaggio, incluso come flusso.

Prerequisiti

Abilitare i flussi

Usare questi passaggi per abilitare i flussi HTTP nell'app per le funzioni in Azure e nei progetti locali:

  1. Se si prevede di trasmettere grandi quantità di dati, modificare l'impostazione FUNCTIONS_REQUEST_BODY_SIZE_LIMIT in Azure. La dimensione massima predefinita consentita è 104857600, che limita le richieste a una dimensione di ~100 MB.

  2. Per lo sviluppo locale, aggiungere FUNCTIONS_REQUEST_BODY_SIZE_LIMIT anche al file local.settings.json.

  3. Aggiungere il codice seguente all'app in qualsiasi file incluso nel campo principale.

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

Esempi di flussi

Questo esempio mostra una funzione attivata da HTTP che riceve dati tramite una richiesta HTTP POST e la funzione trasmette questi dati a un file di output specificato:

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!' };
    },
});

Questo esempio mostra una funzione attivata da HTTP che trasmette il contenuto di un file come risposta alle richieste HTTP GET in ingresso:

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 };
    },
});

Considerazioni sul flusso

  • L'oggetto request.params non è supportato quando si usano flussi HTTP durante l'anteprima. Per altre informazioni e soluzioni alternative suggerite, vedere questo problema di GitHub.

  • Usare request.body per ottenere il massimo vantaggio dall'uso dei flussi. È comunque possibile continuare a usare metodi come request.text(), che restituiscono sempre il corpo come stringa.

Hook

Gli hook non sono supportati nel modello v3. Eseguire l'aggiornamento al modello v4 per usare gli hook.

Usare un hook per eseguire codice in punti diversi del ciclo di vita Funzioni di Azure. Gli hook vengono eseguiti nell'ordine in cui sono registrati e possono essere registrati da qualsiasi file nell'app. Esistono attualmente due ambiti di hook, il livello "app" e il livello di chiamata.

Hook di chiamata

Gli hook di chiamata vengono eseguiti una volta per ogni chiamata della funzione, prima di un preInvocation hook o dopo in un postInvocation hook. Per impostazione predefinita, l'hook viene eseguito per tutti i tipi di trigger, ma è anche possibile filtrare in base al tipo. L'esempio seguente illustra come registrare un hook di chiamata e filtrare in base al tipo di trigger:

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}`
        );
    }
});

Il primo argomento del gestore hook è un oggetto di contesto specifico di tale tipo di hook.

Di seguito sono elencate le proprietà dell'oggetto PreInvocationContext:

Proprietà Descrizione
inputs Argomenti passati alla chiamata.
functionHandler Gestore della funzione per la chiamata. Le modifiche apportate a questo valore influiscono sulla funzione stessa.
invocationContext Oggetto contesto di chiamata passato alla funzione.
hookData Posizione consigliata per archiviare e condividere dati tra hook nello stesso ambito. È consigliabile usare un nome di proprietà univoco in modo che non sia in conflitto con i dati di altri hook.

Di seguito sono elencate le proprietà dell'oggetto PostInvocationContext:

Proprietà Descrizione
inputs Argomenti passati alla chiamata.
result Risultato della funzione. Le modifiche apportate a questo valore influiscono sul risultato complessivo della funzione.
error Errore generato dalla funzione o null/undefined se non è presente alcun errore. Le modifiche apportate a questo valore influiscono sul risultato complessivo della funzione.
invocationContext Oggetto contesto di chiamata passato alla funzione.
hookData Posizione consigliata per archiviare e condividere dati tra hook nello stesso ambito. È consigliabile usare un nome di proprietà univoco in modo che non sia in conflitto con i dati di altri hook.

Hook dell'app

Gli hook dell'app vengono eseguiti una volta per ogni istanza dell'app, durante l'avvio in un appStart hook o durante la terminazione in un appTerminate hook. Gli hook di terminazione dell'app hanno un tempo limitato per l'esecuzione e non vengono eseguiti in tutti gli scenari.

Il runtime Funzioni di Azure attualmente non supporta la registrazione del contesto all'esterno di una chiamata. Usare il pacchetto npm di Application Insights per registrare i dati durante gli hook a livello di app.

L'esempio seguente registra gli hook dell'app:

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

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

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

Il primo argomento del gestore hook è un oggetto di contesto specifico di tale tipo di hook.

Di seguito sono elencate le proprietà dell'oggetto AppStartContext:

Proprietà Descrizione
hookData Posizione consigliata per archiviare e condividere dati tra hook nello stesso ambito. È consigliabile usare un nome di proprietà univoco in modo che non sia in conflitto con i dati di altri hook.

Di seguito sono elencate le proprietà dell'oggetto AppTerminateContext:

Proprietà Descrizione
hookData Posizione consigliata per archiviare e condividere dati tra hook nello stesso ambito. È consigliabile usare un nome di proprietà univoco in modo che non sia in conflitto con i dati di altri hook.

Scalabilità e concorrenza

Per impostazione predefinita, Funzioni di Azure monitora automaticamente il carico nell'applicazione e crea più istanze host per Node.js in base alle esigenze. Funzioni di Azure usa soglie predefinite (non configurabili dall'utente) per diversi tipi di trigger per decidere quando aggiungere istanze, ad esempio l'età dei messaggi e le dimensioni della coda per QueueTrigger. Per altre informazioni, vedere Come funzionano i piani a consumo e Premium.

Questo comportamento di ridimensionamento è sufficiente per molte applicazioni Node.js. Per le applicazioni associate alla CPU, è possibile migliorare ulteriormente le prestazioni usando più processi di lavoro in linguaggio. È possibile aumentare il numero di processi di lavoro per host dal valore predefinito 1 fino a un massimo di 10 usando l'impostazione dell'applicazione FUNCTIONS_WORKER_PROCESS_COUNT . Funzioni di Azure prova quindi a distribuire uniformemente le chiamate di funzioni simultanee tra questi processi di lavoro. Questo comportamento rende meno probabile che una funzione a elevato utilizzo di CPU impedisca l'esecuzione di altre funzioni. L'impostazione si applica a ogni host che Funzioni di Azure crea quando si aumenta la scalabilità orizzontale dell'applicazione per soddisfare la domanda.

Avviso

Usare l'impostazione FUNCTIONS_WORKER_PROCESS_COUNT con cautela. Più processi in esecuzione nella stessa istanza possono causare un comportamento imprevedibile e aumentare i tempi di caricamento delle funzioni. Se si usa questa impostazione, è consigliabile compensare questi svantaggi eseguendo da un file di pacchetto.

Versione del nodo

È possibile visualizzare la versione corrente usata dal runtime registrando process.version da qualsiasi funzione. Vedere supported versions per un elenco delle versioni Node.js supportate da ogni modello di programmazione.

Impostazione della versione del nodo

Il modo in cui si aggiorna la versione Node.js dipende dal sistema operativo in cui viene eseguita l'app per le funzioni.

Durante l'esecuzione in Windows, la versione Node.js viene impostata dall'impostazione dell'applicazione WEBSITE_NODE_DEFAULT_VERSION . Questa impostazione può essere aggiornata usando l'interfaccia della riga di comando di Azure o nella portale di Azure.

Per altre informazioni sulle versioni di Node.js, vedere Versioni supportate.

Prima di aggiornare la versione Node.js, assicurarsi che l'app per le funzioni sia in esecuzione nella versione più recente del runtime di Funzioni di Azure. Se è necessario aggiornare la versione del runtime, vedere Eseguire la migrazione delle app da Funzioni di Azure versione 3.x alla versione 4.x.

Eseguire il comando dell'interfaccia della riga di comando di Azure az functionapp config appsettings set per aggiornare la versione Node.js per l'app per le funzioni in esecuzione in Windows:

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

In questo modo l'applicazione WEBSITE_NODE_DEFAULT_VERSION imposta la versione LTS supportata di ~20.

Dopo aver apportato le modifiche, l'app per le funzioni viene riavviata. Per altre informazioni sul supporto delle funzioni per Node.js, vedere Criteri di supporto del runtime del linguaggio.

Variabili di ambiente

Le variabili di ambiente possono essere utili per i segreti operativi (stringa di connessione, chiavi, endpoint e così via) o impostazioni ambientali come le variabili di profilatura. È possibile aggiungere variabili di ambiente sia negli ambienti locali che nel cloud e accedervi tramite process.env nel codice della funzione.

L'esempio seguente registra la WEBSITE_SITE_NAME variabile di ambiente:

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"]}`);
}

Nell'ambiente di sviluppo locale

Quando si esegue in locale, il progetto di funzioni include un local.settings.json file in cui archiviare le variabili di ambiente nell'oggetto Values .

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

Nell'ambiente cloud di Azure

Quando si esegue in Azure, l'app per le funzioni consente di impostare e usare le impostazioni dell'applicazione, ad esempio le stringa di connessione del servizio, ed espone queste impostazioni come variabili di ambiente durante l'esecuzione.

Esistono diversi modi per aggiungere, aggiornare ed eliminare le impostazioni dell'app per le funzioni:

Le modifiche apportate alle impostazioni dell'app per le funzioni richiedono il riavvio dell'app per le funzioni.

Variabili di ambiente del ruolo di lavoro

Esistono diverse variabili di ambiente di Funzioni specifiche per Node.js:

languageWorkers__node__arguments

Questa impostazione consente di specificare argomenti personalizzati all'avvio del processo di Node.js. Viene spesso usato in locale per avviare il ruolo di lavoro in modalità di debug, ma può essere usato anche in Azure se sono necessari argomenti personalizzati.

Avviso

Se possibile, evitare di usare languageWorkers__node__arguments in Azure perché può avere un effetto negativo sui tempi di inizio a freddo. Invece di usare ruoli di lavoro pre-riscaldati, il runtime deve avviare un nuovo ruolo di lavoro da zero con gli argomenti personalizzati.

logging__logLevel__Worker

Questa impostazione regola il livello di log predefinito per i log di lavoro specifici di Node.js. Per impostazione predefinita, vengono visualizzati solo i log degli avvisi o degli errori, ma è possibile impostarlo su information o debug per diagnosticare i problemi relativi al ruolo di lavoro Node.js. Per altre informazioni, vedere Configurazione dei livelli di log.

Moduli ECMAScript (anteprima)

Nota

Poiché i moduli ECMAScript sono attualmente una funzionalità di anteprima in Node.js 14 o versione successiva in Funzioni di Azure.

I moduli ECMAScript (moduli ES) sono il nuovo sistema di moduli standard ufficiale per Node.js. Finora, gli esempi di codice in questo articolo usano la sintassi CommonJS. Quando si esegue Funzioni di Azure in Node.js 14 o versione successiva, è possibile scegliere di scrivere le funzioni usando la sintassi dei moduli ES.

Per usare i moduli ES in una funzione, modificare il nome file in modo da usare un'estensione .mjs . L'esempio di file index.mjs seguente è una funzione attivata da HTTP che usa la sintassi dei moduli ES per importare la uuid libreria e restituire un valore.

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
});

Configurare il punto di ingresso della funzione

È possibile usare le proprietà scriptFile e entryPoint di function.json per configurare il percorso e il nome della funzione esportata. La scriptFile proprietà è obbligatoria quando si usa TypeScript e deve puntare al codice JavaScript compilato.

uso di scriptFile

Per impostazione predefinita, viene eseguita una funzione JavaScript da index.js, un file che condivide la stessa directory padre del file function.json corrispondente.

È possibile usare scriptFile per ottenere una struttura di cartelle simile a quella nell'esempio seguente:

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

Il file function.json per myFirstFunction deve includere una proprietà scriptFile che punta al file con la funzione esportata da eseguire.

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

uso di entryPoint

Nel modello v3, una funzione deve essere esportata usando module.exports per poter essere trovata ed eseguita. Per impostazione predefinita, la funzione eseguita quando viene attivata è l'unica esportazione dal file, ovvero l'esportazione denominata run o quella denominata index. Nell'esempio seguente viene function.json impostato entryPoint su un valore personalizzato, "logHello":

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

module.exports = { logHello };

Debug locale

È consigliabile usare VS Code per il debug locale, che avvia automaticamente il processo di Node.js in modalità di debug e si collega automaticamente al processo. Per altre informazioni, vedere Eseguire la funzione in locale.

Se si usa uno strumento diverso per il debug o si vuole avviare manualmente il processo di Node.js in modalità di debug, aggiungere "languageWorkers__node__arguments": "--inspect" in local.settings.json.Values L'argomento --inspect indica Node.js di restare in ascolto di un client di debug sulla porta 9229 per impostazione predefinita. Per altre informazioni, vedere la guida al debug Node.js.

Consigli

Questa sezione descrive diversi modelli di impatto per Node.js app che ti consigliamo di seguire.

Scegliere i piani di servizio app con una singola vCPU

Quando si crea un'app per le funzioni che usa il piano di servizio app, è consigliabile selezionare un piano con una singola vCPU anziché uno con più vCPU. Attualmente, Funzioni esegue Node.js funzioni in modo più efficiente nelle macchine virtuali a singola vCPU e l'uso di macchine virtuali di dimensioni maggiori non produce i miglioramenti previsti delle prestazioni. Quando necessario, è possibile aumentare manualmente il numero di istanze di macchine virtuali con scalabilità orizzontale aggiungendo altre istanze di VM a vCPU singola oppure abilitare la scalabilità automatica. Per altre informazioni, vedere Scalare il conteggio delle istanze manualmente o automaticamente.

Eseguire da un file di pacchetto

Quando si sviluppano Funzioni di Azure nel modello di hosting serverless, l'avvio a freddo è una realtà. L'avvio a freddo si riferisce alla prima volta che l'app per le funzioni inizia dopo un periodo di inattività, richiedendo più tempo per l'avvio. Per Node.js app con alberi delle dipendenze di grandi dimensioni, in particolare, l'avvio a freddo può essere significativo. Per velocizzare il processo di avvio a freddo eseguire le funzioni come un file di pacchetto quando possibile. Per impostazione predefinita, molti metodi di distribuzione usano questo modello, ma se si verifica un avvio a freddo di grandi dimensioni, è consigliabile verificare di essere in esecuzione in questo modo.

Usare un singolo client statico

Quando si usa un client specifico del servizio in un'applicazione Funzioni di Azure, non creare un nuovo client con ogni chiamata di funzione perché è possibile raggiungere i limiti di connessione. Creare invece un singolo client statico nell'ambito globale. Per altre informazioni, vedere Gestione delle connessioni in Funzioni di Azure.

Usare async ed await

Quando si scrivono Funzioni di Azure in Node.js, è necessario scrivere codice usando le async parole chiave e await . La scrittura di codice usando async e await invece di callback o .then con .catch promise consente di evitare due problemi comuni:

  • Generazione di eccezioni non rilevate che causano un arresto anomalo del processo di Node.js, che potrebbero influire sull'esecuzione di altre funzioni.
  • Comportamento imprevisto, ad esempio log mancanti da context.log, causato da chiamate asincrone non attese correttamente.

Nell'esempio seguente il metodo fs.readFile asincrono viene richiamato con una funzione di callback error-first come secondo parametro. Questo codice causa entrambi i problemi menzionati in precedenza. Un'eccezione non rilevata in modo esplicito nell'ambito corretto può arrestare l'intero processo (problema 1). La restituzione senza garantire il completamento del callback indica che la risposta HTTP a volte avrà un corpo vuoto (problema 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 };
    },
});

Nell'esempio seguente il metodo fs.readFile asincrono viene richiamato con una funzione di callback error-first come secondo parametro. Questo codice causa entrambi i problemi menzionati in precedenza. Un'eccezione non rilevata in modo esplicito nell'ambito corretto può arrestare l'intero processo (problema 1). La chiamata al metodo deprecato context.done() all'esterno dell'ambito del callback può segnalare che la funzione viene completata prima della lettura del file (problema 2). In questo esempio, la chiamata context.done() troppo presto comporta la mancanza di voci di log che iniziano con 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();
}

Usare le async parole chiave e await per evitare entrambi questi problemi. La maggior parte delle API nell'ecosistema Node.js è stata convertita per supportare le promesse in qualche modo. Ad esempio, a partire dalla versione 14, Node.js fornisce un'API fs/promises per sostituire l'API fs di callback.

Nell'esempio seguente, tutte le eccezioni non gestite generate durante l'esecuzione della funzione non superano solo la singola chiamata che ha generato l'eccezione. La await parola chiave indica che i passaggi seguenti vengono readFile eseguiti solo dopo il completamento.

// 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;
        }
    },
});

Con async e awaitnon è necessario chiamare il context.done() callback.

// 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}`);
}

Risoluzione dei problemi

Vedere la guida alla risoluzione dei problemi di Node.js.

Passaggi successivi

Per ulteriori informazioni, vedi le seguenti risorse: