Condividi tramite


Guida per sviluppatori di Node.js di Funzioni di Azure

Questa guida è un'introduzione allo sviluppo di Funzioni di Azure con JavaScript o TypeScript. L'articolo presuppone che sia già stata letta la Guida per gli sviluppatori di 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 pacchetto npm @azure/functions 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 per 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 pacchetto npm @azure/functions. 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 di 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, vedere 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, vedere 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, 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 dell'app e le stringhe di connessione quando viene eseguito 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, 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 dell'app e le stringhe di connessione quando viene eseguito 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 file function.json 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 file index.js nella stessa cartella di function.json. Se si usa TypeScript, è necessario usare la proprietà scriptFile 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 async function nel modello v3. È possibile esportare una funzione sincrona, ma è necessario chiamare context.done() per segnalare che la funzione è stata completata, che è deprecata e non consigliata.

Alla funzione viene passata una chiamata context come 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 main in package.json. È possibile impostare il campo main su un singolo file o su più file usando un modello glob. La tabella seguente mostra i valori di esempio per il campo main:

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 modulo npm @azure/functions e chiamare il metodo specifico del tipo di trigger. Il primo argomento durante la registrazione di una funzione è il nome della funzione. Il secondo argomento è un oggetto options 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é un oggetto options.

La registrazione di una funzione può essere eseguita da qualsiasi file del progetto, purché tale file venga caricato (direttamente o indirettamente) in base al campo main nel file package.json. 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 vengono configurati nei file di function.json e vengono definiti anche associazioni.

Input

Gli input sono associazioni con direction impostate su in. La differenza principale tra un trigger e un input secondario è che il type per un trigger termina in Trigger, ad esempio il tipo blobTrigger rispetto al tipo blob. 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:

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

    module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
    
  • Come proprietà di context.bindings: Usare la chiave corrispondente alla proprietà name 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 associazioni con direction impostate su out e possono essere impostate in diversi modi:

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

    {
        "name": "$return",
        "type": "http",
        "direction": "out"
    }
    
    module.exports = async function (context, request) {
        return {
            body: "Hello, world!"
        };
    }
    
  • [Consigliato per più output] Restituire 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 nel 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 su context.bindings: Se non si usa una funzione asincrona o non si vogliono usare le opzioni precedenti, è possibile impostare i valori direttamente in 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 proprietà dataType in un'associazione di input per modificare il tipo di input, ma presenta alcune limitazioni:

  • In Node.js, sono supportati solo string e binary (stream non lo è)
  • Per gli input HTTP, la proprietà dataType 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 è un string, ma se si imposta dataType su binary, 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 all'esterno del contesto del modello di programmazione Node.js. Prima della versione 4 del modello, queste associazioni sono state configurate nei file function.json.

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 nell'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 della 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 modulo @azure/functions. 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 nell'argomento options durante la registrazione di una funzione. Gli oggetti input e output esportati dal modulo @azure/functions 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 oggetti app, trigger, input e output esportati dal modulo @azure/functions forniscono metodi specifici del tipo per la maggior parte dei tipi. Per tutti i tipi non supportati, viene fornito un metodo generic per consentire di specificare manualmente la configurazione. Il metodo generic 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 una chiamata context oggetto, usata 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 L'ID della chiamata di funzione corrente.
executionContext Vedere il contesto di esecuzione.
bindings Vedere le associazioni.
bindingData Metadati relativi all'input del trigger per questa chiamata, non incluso il valore stesso. Ad esempio, un trigger dell'hub eventi ha una proprietà enqueuedTimeUtc.
traceContext Contesto per la traccia distribuita. Per ulteriori informazioni, vedere Trace Context.
bindingDefinitions La 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 L'ID della chiamata di funzione corrente.
functionName Nome della funzione richiamata. Il nome della cartella contenente il file function.json determina il nome della funzione.
functionDirectory Cartella contenente il file function.json.
retryContext Vedere il 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. Un valore di -1 indica di 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 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 metodo context.done è 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 si await 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 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 classe InvocationContext ha le proprietà seguenti:

Proprietà Descrizione
invocationId L'ID della chiamata di 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 il 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 proprietà enqueuedTimeUtc.
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. Un valore di -1 indica di 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 Azure Application 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 il monitoraggio di 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 essere associati a una funzione specifica. È consigliabile usare context per la registrazione anziché 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 context.log predefinito, 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 proprietà logging.logLevel nel file host.json. 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 oggetti HttpRequest e HttpResponse per rappresentare i messaggi HTTP. Le classi rappresentano un subset del recupero standard, usando il pacchetto undici di 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}"`);
    
  • Dalla proprietà context.req:

    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 richiesta.
headers Record<string, string> Intestazioni di richiesta HTTP. Questo oggetto fa distinzione tra maiuscole e minuscole. È consigliabile usare invece request.getHeader('header-name'), 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 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 proprietà context.res:

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • Restituire la risposta: se la funzione è asincrona e si imposta il nome dell'associazione su $return in function.json, è possibile restituire la risposta direttamente 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!` };
    
  • Chiamare context.res.send(): questa opzione è deprecata. Chiama in modo implicito context.done() 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 context.res predefinito usa l'interfaccia HttpResponseFull, che supporta i metodi seguenti oltre alle proprietà HttpResponseSimple:

metodo Descrizione
status() Imposta lo stato.
setHeader() Imposta un campo di intestazione. NOTA: anche res.set() e res.header() sono supportati ed eseguono la stessa operazione.
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 chiama 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 chiama 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 è terminata.

La risposta può essere impostata in diversi modi:

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

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

    L'interfaccia HttpResponseInit ha le proprietà seguenti:

    Proprietà Type Descrizione
    body BodyInit (facoltativo) Corpo della risposta HTTP come uno dei ArrayBuffer, AsyncIterable<Uint8Array>, Blob, FormData, Iterable<Uint8Array>, NodeJS.ArrayBufferView, URLSearchParams, null o string.
    jsonBody any (facoltativo) Corpo della risposta HTTP serializzabile in JSON. Se impostata, la proprietà HttpResponseInit.body 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 classe HttpResponse accetta un HttpResponseInit facoltativo 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

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.

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 HttpRequest e HttpResponse esistenti nel modello di programmazione v4 supportano già vari modi per gestire il corpo del messaggio, tra cui 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 per il corpo è 104857600, che limita le richieste a una dimensione di circa 100 MB.

  2. Per lo sviluppo locale, aggiungere anche FUNCTIONS_REQUEST_BODY_SIZE_LIMIT 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 };
    },
});

Per un'app di esempio pronta per l'esecuzione con flussi, vedere questo esempio in GitHub.

Considerazioni sul flusso

  • 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 nel ciclo di vita di 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 in un hook preInvocation o dopo in un hook postInvocation. 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 L’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 L’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 hook appStart o durante la terminazione in un hook appTerminate. Gli hook di terminazione dell'app hanno un tempo limitato per l'esecuzione e non vengono eseguiti in tutti gli scenari.

Il runtime di 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 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 creato da Funzioni di Azure 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 per compensare questi svantaggi in esecuzione da un file di pacchetto.

Versione del nodo

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

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 nel 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 az functionapp config appsettings set dell'interfaccia della riga di comando di Azure 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> 

Imposta l'impostazione dell'applicazione WEBSITE_NODE_DEFAULT_VERSION 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 Language Runtime support policy.

Variabili di ambiente

Le variabili di ambiente possono essere utili per i segreti operativi (stringhe 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 variabile di ambiente 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"]}`);
}

Nell’ambiente di sviluppo locale

Quando si esegue in locale, il progetto di funzioni include un file local.settings.json, in cui le variabili di ambiente vengono archiviate 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 stringhe 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.

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 eseguono 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 libreria uuid 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 proprietà scriptFile è necessaria 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 è necessario esportare una funzione 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. L'esempio seguente imposta entryPoint in function.json 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 Values all’interno di local.settings.json. L'argomento --inspect indica Node.js di rimanere 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 le app Node.js che 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. Se necessario, è possibile aumentare manualmente il numero di istanze aggiungendo altre istanze di macchine virtuali con una singola vCPU 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à. Avvio a freddo fa riferimento alla prima volta che l'app per le funzioni inizia dopo un periodo di inattività, richiedendo più tempo per l'avvio. Per app Node.js 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 scrive Funzioni di Azure in Node.js, è necessario scrivere codice usando le parole chiave async e await. Scrivere codice usando async e await anziché callback o .then e .catch con Promise consente di evitare due problemi comuni:

  • Generazione di eccezioni non rilevate che arrestano in modo anomalo il processo di Node.js, che potenzialmente influisce 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 asincrono fs.readFile 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 asincrono fs.readFile 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, chiamare context.done() troppo presto genera voci di log mancanti a partire da 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 parole chiave async 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 di callback fs.

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 parola chiave await indica che i passaggi seguenti readFile vengono 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 await non è necessario chiamare anche il callback 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}`);
}

Risoluzione dei problemi

Vedere la guida alla risoluzione dei problemi Node.js.

Passaggi successivi

Per ulteriori informazioni, vedi le seguenti risorse: