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 infunction.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 infunction.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
infunction.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 incontext.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
ebinary
(stream
non lo è) - Per gli input HTTP, la proprietà
dataType
viene ignorata. Usare invece le proprietà sull'oggettorequest
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 incontext.bindings
o "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
infunction.json
, è possibile restituire la risposta direttamente anziché impostarla sucontext
.{ "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 incontext.bindings
o "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 implicitocontext.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
ostring
.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 unHttpResponseInit
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
- Il pacchetto
@azure/functions
npm versione 4.3.0 o successiva. - Runtime di Funzioni di Azure versione 4.28 o successiva.
- Azure Functions Core Tools versione 4.0.5530 o successiva, che contiene la versione di runtime corretta.
Abilitare i flussi
Usare questi passaggi per abilitare i flussi HTTP nell'app per le funzioni in Azure e nei progetti locali:
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.Per lo sviluppo locale, aggiungere anche
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
al file local.settings.json.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 comerequest.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:
- Nel portale di Azure.
- Tramite l'uso dell'interfaccia della riga di comando di Azure
- Usando Azure PowerShell.
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: