API TypeScript serverless: archiviare i dati in MongoDB con Funzioni di Azure
Creare un'API per le funzioni di Azure per archiviare i dati con l'API Mongoose in Azure Cosmos DB, quindi distribuire l'applicazione Function nel cloud di Azure per l'hosting con un endpoint HTTP pubblico.
Nota
Questo articolo usa il modello di programmazione node.js v4 Funzioni di Azure attualmente in anteprima.
Preparare l'ambiente di sviluppo
Installare il software seguente:
- Creare una sottoscrizione gratuita di Azure
- Installare Node.js LTS v18+
- TypeScript v4+
- Azurite installato a livello globale per l'archiviazione di sviluppo locale
- Funzioni di Azure Runtime v4.16+
- Funzioni di Azure Core Tools v4.0.5095+ (se in esecuzione in locale) installato a livello globale per lo sviluppo locale
- Installare Visual Studio Code e usare le estensioni seguenti:
- Azure Resources (Risorse di Azure)
- Funzioni di Azure
- Database di Azure
1. Accedere ad Azure in Visual Studio Code
Se si usano già estensioni dei servizi di Azure, si è già connessi ed è possibile ignorare questo passaggio.
Dopo aver installato un'estensione del servizio di Azure in Visual Studio Code, è necessario accedere all'account Azure.
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Nella sezione Risorse selezionare Accedi ad Azure e seguire le istruzioni.
Dopo l'accesso, verificare che l'indirizzo di posta elettronica dell'account Azure venga visualizzato nella barra di stato e che le sottoscrizioni siano visualizzate in Azure Explorer:
2. Creare un gruppo di risorse di Azure
Un gruppo di risorse è una raccolta di risorse basata sull'area. Creando un gruppo di risorse, quindi creando risorse in tale gruppo, alla fine dell'esercitazione, è possibile eliminare il gruppo di risorse senza dover eliminare ogni risorsa singolarmente.
Creare una nuova cartella nel sistema locale da usare come radice del progetto di funzioni di Azure.
Aprire questa cartella in Visual Studio Code.
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Trovare la sottoscrizione in Risorse e selezionare l'icona + e quindi selezionare Crea gruppo di risorse.
Usare la tabella seguente per completare i prompt:
Richiesta Valore Immettere il nome del nuovo gruppo di risorse. azure-tutorial
Selezionare una posizione per le nuove risorse. Selezionare un'area geografica vicina all'utente.
3. Creare l'app per le funzioni locale
Creare un'applicazione Funzioni di Azure locale (serverless) che contiene una funzione trigger HTTP.
In Visual Studio Code aprire il riquadro comandi (CTRL + MAIUSC + P).
Cercare e selezionare Funzioni di Azure: Crea nuovo progetto.
Usare la tabella seguente per completare la creazione del progetto funzione di Azure locale:
Richiesta Valore Note Selezionare la cartella che conterrà il progetto di funzione Selezionare la cartella corrente (predefinita). Selezionare una lingua TypeScript Selezionare un modello di programmazione TypeScript Modello V4 (anteprima) Selezionare un modello per la prima funzione del progetto Trigger HTTP L'API viene richiamata con una richiesta HTTP. Specificare un nome di funzione blogposts
La route API è /api/blogposts
Quando Visual Studio Code crea il progetto, visualizzare il codice DELL'API nel
./src/functions/blogposts.ts
file.import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; export async function blogposts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function processed request for url "${request.url}"`); const name = request.query.get('name') || await request.text() || 'world'; return { body: `Hello, ${name}!` }; }; app.http('blogposts', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: blogposts });
Questo codice è boilerplate standard nel nuovo modello di programmazione v4. Non è progettato per indicare l'unico modo per scrivere un livello API con POST e GET.
Sostituire il codice precedente con il codice seguente per consentire solo alle richieste GET di restituire tutti i post di blog.
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; // curl --location 'http://localhost:7071/api/blogposts' --verbose export async function getBlogPosts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); // Empty array for now ... will fix later const blogposts = []; return { status: 200, jsonBody: { blogposts } }; }; app.get('getBlogPosts', { route: "blogposts", authLevel: 'anonymous', handler: getBlogPosts });
Esistono diverse modifiche del modello di programmazione Node.js v4 Funzioni di Azure a questo codice da tenere presente:
- Il nome della funzione di , che indica che si tratta di
getBlobPosts
una richiesta GET, consente di isolare la funzione nei log. - La
route
proprietà è impostata sublogposts
, che fa parte della route API predefinita fornita,/api/blogposts
. - La
methods
proprietà è stata rimossa e non è necessaria perché l'usoapp
dell'oggetto di indica che si tratta diget
una richiesta GET. Le funzioni del metodo sono elencate di seguito. Se si dispone di un metodo diverso, è possibile tornare a usando lamethods
proprietà .deleteRequest()
get()
patch()
post()
put()
- Il nome della funzione di , che indica che si tratta di
4. Avviare l'emulatore di archiviazione locale Di Azurite
Lo sviluppo di funzioni nel computer locale richiede un emulatore Archiviazione (gratuito) o un account Archiviazione di Azure (a pagamento).
In un terminale separato avviare l'emulatore di archiviazione locale di Azurite .
azurite --silent --location ./azurite --debug ./azurite/debug.log
Questa operazione è necessaria per eseguire il Funzioni di Azure in locale usando un emulatore di Archiviazione di Azure locale. L'emulatore di archiviazione locale viene specificato nel local.settings.json
file con la proprietà AzureWebJobs Archiviazione con il valore UseDevelopmentStorage=true
.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing"
}
}
La azurite
sottocartella è già stata aggiunta al .gitignore
file.
5. Eseguire la funzione serverless locale
Eseguire il progetto di Funzioni di Azure in locale per testarlo prima della distribuzione in Azure.
In Visual Studio Code impostare un punto di interruzione sull'istruzione
return
alla fine della funzione getBlogPosts .In Visual Studio Code premere F5 per avviare il debugger e collegarsi all'host Funzioni di Azure.
È anche possibile usare il comando di menu Debug>Avvia debug.
L'output viene visualizzato nel pannello Terminale .
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Nella sezione Area di lavoro trovare ed espandere il progetto locale ->Functions ->getBlogPosts.
Fare clic con il pulsante destro del mouse sul nome della funzione, getBlogPosts e quindi scegliere Copia URL funzione.
Nel browser incollare l'URL e selezionare Invio o usare il comando cURL seguente nel terminale:
curl http://localhost:7071/api/blogposts --verbose
La risposta di una matrice vuota di post di blog viene restituita come:
* Trying 127.0.0.1:7071... * Connected to localhost (127.0.0.1) port 7071 (#0) > GET /api/blogposts HTTP/1.1 > Host: localhost:7071 > User-Agent: curl/7.88.1 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: application/json < Date: Mon, 08 May 2023 17:35:24 GMT < Server: Kestrel < Transfer-Encoding: chunked < {"blogposts":[]}* Connection #0 to host localhost left intact
In VS Code arrestare il debugger Maiusc + F5.
6. Creare l'app per le funzioni di Azure in Visual Studio Code
In questa sezione viene creata una risorsa cloud dell'app per le funzioni e le risorse correlate nella sottoscrizione di Azure.
In Visual Studio Code aprire il riquadro comandi (CTRL + MAIUSC + P).
Cercare e selezionare Funzioni di Azure: Creare un'app per le funzioni in Azure (avanzate).
Quando richiesto, immettere le informazioni seguenti:
Richiesta Selezione Immettere un nome univoco globale per l'app per le funzioni Digitare un nome valido in un percorso URL, ad esempio first-function
. Anteporre 3 caratteri per rendere l'URL univoco a livello globale. Il nome digitato viene convalidato per assicurarsi che sia univoco in Funzioni di Azure.Selezionare uno stack di runtime Scegliere Node.js 18 LTS o una versione più recente. Selezionare un sistema operativo Scegliere Linux. Selezionare un gruppo di risorse per le nuove risorse Creare un nuovo gruppo di risorse denominato azure-tutorial-first-function. Questo gruppo di risorse avrà infine diverse risorse: Funzione di Azure, Archiviazione di Azure e l'API Cosmos DB per MongoDB. Selezionare un piano di hosting Scegliere Consumo. Selezionare un account di archiviazione Selezionare Crea un nuovo account di archiviazione e accettare il nome predefinito. Selezionare una risorsa di Application Insights per l'app. Selezionare Crea nuova risorsa di Application Insights e accettare il nome predefinito. Attendere fino a quando la notifica conferma che l'app è stata creata.
7. Distribuire l'app per le funzioni di Azure in Azure in Visual Studio Code
Importante
La distribuzione in un'app per le funzioni esistente sovrascrive sempre il contenuto di tale app in Azure.
- Scegliere l'icona di Azure nella barra attività, quindi nell'area Risorse fare clic con il pulsante destro del mouse sulla risorsa dell'app per le funzioni e selezionare Deploy to Function App (Distribuisci nell'app per le funzioni).
- Se viene chiesto se si è certi di voler distribuire, selezionare Distribuisci.
- Al termine della distribuzione, viene visualizzata una notifica con diverse opzioni. Selezionare Visualizza output per visualizzare i risultati. Se non si riesce a visualizzare la notifica, selezionare l'icona della campana nell'angolo in basso a destra per visualizzarla di nuovo.
8. Aggiungere l'impostazione dell'applicazione all'app cloud
Scegliere l'icona di Azure nella barra attività, quindi nell'area Risorse espandere la risorsa dell'app per le funzioni e fare clic con il pulsante destro del mouse su Selezionare Application Impostazioni.
Selezionare Aggiungi nuova impostazione e aggiungere l'impostazione seguente per abilitare il modello di programmazione Node.js v4 (anteprima).
Impostazione Valore AzureWebJobsFeatureFlags EnableWorkerIndexing
9. Eseguire la funzione serverless remota
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Nella sezione Risorse espandere la risorsa dell'app per le funzioni di Azure. Fare clic con il pulsante destro del mouse sul nome della funzione e scegliere Copia URL funzione.
Incollare l'URL in un browser. La stessa matrice vuota viene restituita come quando è stata eseguita la funzione in locale.
{"blogposts":[]}
10. Aggiungere l'integrazione dell'API Azure Cosmos DB per MongoDB
Azure Cosmos DB offre un'API MongoDB per fornire un punto di integrazione familiare.
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Nella sezione Risorse selezionare quindi +Crea server di database. Usare la tabella seguente per completare le richieste di creazione di una nuova risorsa di Azure Cosmos DB.
Richiesta Valore Note Selezionare un server di database di Azure API Azure Cosmos DB per MongoDB Specificare un nome di account Azure Cosmos DB. cosmosdb-mongodb-database
Anteporre tre caratteri per creare un nome univoco. Il nome diventa parte dell'URL dell'API. Selezionare un modello di capacità. Senza server Selezionare un gruppo di risorse per le nuove risorse. azure-tutorial-first-function Selezionare il gruppo di risorse creato in una sezione precedente. Select a location for new resources. Selezionare l'area consigliata.
11. Installare la dipendenza mongoose
In un terminale di Visual Studio Code, CTRL + MAIUSC + ` e quindi installare il pacchetto npm:
npm install mongoose
12. Aggiungere il codice mongoose per i post di blog
In Visual Studio Code creare una sottodirectory denominata lib in
./src/
, creare un file denominato./database.ts
e copiarne il codice seguente.import { Schema, Document, createConnection, ConnectOptions, model, set } from 'mongoose'; const connectionString = process.env.MONGODB_URI; console.log('connectionString', connectionString); const connection = createConnection(connectionString, { useNewUrlParser: true, useUnifiedTopology: true, autoIndex: true } as ConnectOptions); export interface IBlogPost { author: string title: string body: string } export interface IBlogPostDocument extends IBlogPost, Document { id: string created: Date } const BlogPostSchema = new Schema({ id: Schema.Types.ObjectId, author: String, title: String, body: String, created: { type: Date, default: Date.now } }); BlogPostSchema.set('toJSON', { transform: function (doc, ret, options) { ret.id = ret._id; delete ret._id; delete ret.__v; } }); export const BlogPost = model<IBlogPostDocument>('BlogPost', BlogPostSchema); connection.model('BlogPost', BlogPostSchema); export default connection;
In Visual Studio Code aprire il
./src/functions/blogposts
file e sostituire il codice dell'intero file con quanto segue:import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; import connection from '../lib/database'; // curl --location 'http://localhost:7071/api/blogposts' --verbose export async function getBlogPosts(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); const blogposts = await connection.model('BlogPost').find({}); return { status: 200, jsonBody: { blogposts } }; }; app.get('getBlogPosts', { route: "blogposts", authLevel: 'anonymous', handler: getBlogPosts });
13. Aggiungere stringa di connessione all'app locale
In Azure Explorer di Visual Studio Code selezionare la sezione Azure Cosmos DB ed espandere fino a selezionare la nuova risorsa facendo clic con il pulsante destro del mouse.
Selezionare Copia stringa di connessione.
In Visual Studio Code usare Esplora file per aprire
./local.settings.json
.Aggiungere una nuova proprietà denominata
MONGODB_URI
e incollare il valore del stringa di connessione.{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "node", "AzureWebJobsFeatureFlags": "EnableWorkerIndexing", "MONGODB_URI": "mongodb://...." } }
Segreti nel
./local.settings.json
file:- Non viene distribuito in Azure perché è incluso nel
./.funcignore
file. - Non viene archiviato nel controllo del codice sorgente perché è incluso nel
./.gitignore
file.
- Non viene distribuito in Azure perché è incluso nel
Eseguire l'applicazione in locale e testare l'API con lo stesso URL nella sezione precedente.
14. Aggiungere stringa di connessione all'app remota
- In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
- Nella sezione Risorse individuare l'istanza di Azure Cosmos DB. Fare clic con il pulsante destro del mouse sulla risorsa e scegliere Copia stringa Connessione ion.
- Nella stessa sezione Risorse trovare l'app per le funzioni ed espandere il nodo.
- Fare clic con il pulsante destro del mouse su Application Impostazioni e scegliere Aggiungi nuova impostazione.
- Immettere il nome
MONGODB_URI
dell'impostazione dell'app e selezionare Invio. - Incollare il valore copiato e premere INVIO.
15. Aggiungere API per la creazione, l'aggiornamento e l'eliminazione dei post di blog
In Visual Studio Code usare il riquadro comandi per trovare e selezionare Funzioni di Azure: Crea funzione.
Selezionare trigger HTTP e denominarlo
blogpost
(singolare).Copiare il codice seguente nel file .
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; import connection, { IBlogPost, IBlogPostDocument } from '../lib/database'; // curl -X POST --location 'http://localhost:7071/api/blogpost' --header 'Content-Type: application/json' --data '{"author":"john","title":"my first post", "body":"learn serverless node.js"}' --verbose export async function addBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function addBlogPost processed request for url "${request.url}"`); const body = await request.json() as IBlogPost; const blogPostResult = await connection.model('BlogPost').create({ author: body?.author, title: body?.title, body: body?.body }); return { status: 200, jsonBody: { blogPostResult } }; }; // curl -X PUT --location 'http://localhost:7071/api/blogpost/64568e727f7d11e09eab473c' --header 'Content-Type: application/json' --data '{"author":"john jones","title":"my first serverless post", "body":"Learn serverless Node.js with Azure Functions"}' --verbose export async function updateBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function updateBlogPost processed request for url "${request.url}"`); const body = await request.json() as IBlogPost; const id = request.params.id; const blogPostResult = await connection.model('BlogPost').updateOne({ _id: id }, { author: body?.author, title: body?.title, body: body?.body }); if(blogPostResult.matchedCount === 0) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPostResult } }; }; // curl --location 'http://localhost:7071/api/blogpost/6456597918547e37d515bda3' --verbose export async function getBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function getBlogPosts processed request for url "${request.url}"`); console.log('request.params.id', request.params.id) const id = request.params.id; const blogPost = await connection.model('BlogPost').findOne({ _id: id }); if(!blogPost) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPost } }; }; // curl --location 'http://localhost:7071/api/blogpost/6456597918547e37d515bda3' --request DELETE --header 'Content-Type: application/json' --verbose export async function deleteBlogPost(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function deleteBlogPost processed request for url "${request.url}"`); const id = request.params.id; const blogPostResult = await connection.model('BlogPost').deleteOne({ _id: id }); if(blogPostResult.deletedCount === 0) { return { status: 404, jsonBody: { message: 'Blog post not found' } }; } return { status: 200, jsonBody: { blogPostResult } }; }; app.get('getBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: getBlogPost }); app.post('postBlogPost', { route: "blogpost", authLevel: 'anonymous', handler: addBlogPost }); app.put('putBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: updateBlogPost }); app.deleteRequest('deleteBlogPost', { route: "blogpost/{id}", authLevel: 'anonymous', handler: deleteBlogPost });
Avviare di nuovo la funzione locale con il debugger. Sono disponibili le API seguenti:
deleteBlogPost: [DELETE] http://localhost:7071/api/blogpost/{id} getBlogPost: [GET] http://localhost:7071/api/blogpost/{id} getBlogPosts: [GET] http://localhost:7071/api/blogposts postBlogPost: [POST] http://localhost:7071/api/blogpost putBlogPost: [PUT] http://localhost:7071/api/blogpost/{id}
Usare l'API
blogpost
(singolare) da un comando cURL per aggiungere alcuni post di blog.curl -X POST --location 'http://localhost:7071/api/blogpost' --header 'Content-Type: application/json' --data '{"author":"john","title":"my first post", "body":"learn serverless node.js"}' --verbose
Usare l'API
blogposts
(plurale) da un comando cURL per ottenere i post di blog.curl http://localhost:7071/api/blogposts --verbose
16. Visualizzare tutti i dati con l'estensione Visual Studio Code per Azure Cosmos DB
In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
Nella sezione Risorse fare clic con il pulsante destro del mouse sul database Azure Cosmos DB e scegliere Aggiorna.
Espandere il database di test e il nodo della raccolta blogposts per visualizzare i documenti.
Selezionare uno degli elementi elencati per visualizzare i dati nell'istanza di Azure Cosmos DB.
17. Ridistribuire l'app per le funzioni per includere il codice del database
- In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
- Nella sezione Risorse fare clic con il pulsante destro del mouse sull'app per le funzioni di Azure e selezionare Distribuisci nell'app per le funzioni.
- Nella finestra popup in cui viene chiesto se si è certi di voler distribuire selezionare Distribuisci.
- Attendere il completamento della distribuzione prima di continuare.
18. Usare la funzione di Azure basata sul cloud
- Sempre in Azure Explorer, nell'area Funzioni, seleziona ed espande la funzione e quindi il nodo Funzioni , che elenca le API
- Fare clic con il pulsante destro del mouse su una delle API e scegliere Copia URL funzione.
- Modificare i comandi cURL precedenti per usare l'URL remoto anziché l'URL locale. Eseguire i comandi per testare l'API remota.
19. Eseguire query sui log delle funzioni di Azure
Per eseguire ricerche nei log, usare il portale di Azure.
In Visual Studio Code selezionare Azure Explorer, quindi in Funzioni fare clic con il pulsante destro del mouse sull'app per le funzioni e quindi scegliere Apri nel portale.
Verrà aperto il portale di Azure alla funzione di Azure.
In Impostazioni selezionare Application Insights, quindi selezionare Visualizza dati di Application Insights.
Questo collegamento consente di passare alla risorsa metriche separata creata al momento della creazione della funzione di Azure con Visual Studio Code.
Nella sezione Monitoraggio selezionare Log. Se viene visualizzata una finestra popup Query , selezionare la X nell'angolo superiore destro del popup per chiuderla.
Nel riquadro Nuova query 1 fare doppio clic sulla tabella delle tracce nella scheda Tabelle.
Viene immessa la query Kusto nella
traces
finestra di query.Modificare la query per cercare i log personalizzati:
traces | where message startswith "***"
Selezionare Esegui.
Se il log non visualizza risultati, potrebbe verificarsi un ritardo di pochi minuti tra la richiesta HTTP alla funzione di Azure e la disponibilità dei log in Kusto. Attendere alcuni minuti ed eseguire di nuovo la query.
Non è necessario eseguire alcuna operazione aggiuntiva per ottenere queste informazioni di registrazione:
- Codice usato dalla
context.log
funzione fornita dal framework funzione.context
Usando , invece diconsole
, la registrazione può essere filtrata in base alla singola funzione specifica. Ciò è utile se l'app per le funzioni ha molte funzioni. - L'app per le funzioni ha aggiunto Application Insights.
- Lo strumento query Kusto è incluso nella portale di Azure.
- È possibile selezionare
traces
invece di dover imparare a scrivere una query Kusto per ottenere anche le informazioni minime dai log.
- Codice usato dalla
20. Pulire le risorse
Poiché è stato usato un singolo gruppo di risorse, è possibile eliminare tutte le risorse eliminando il gruppo di risorse.
- In Visual Studio Code aprire Esplora risorse di Azure selezionando l'icona di Azure nella barra laterale primaria o usando la scelta rapida da tastiera (MAIUSC + ALT + A).
- Cercare e selezionare Azure: Raggruppa per gruppo di risorse.
- Fare clic con il pulsante destro del mouse sul gruppo di risorse e scegliere Elimina gruppo di risorse.
- Immettere il nome del gruppo di risorse per confermare l'eliminazione.
Codice sorgente disponibile
Codice sorgente completo per questa app per le funzioni di Azure:
Passaggi successivi
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per