Connettere Funzioni di Azure ad Archiviazione di Azure con gli strumenti da riga di comando

In questo articolo si integra una coda di Archiviazione di Azure con la funzione e l'account di archiviazione creati nell'argomento di avvio rapido precedente. È possibile ottenere questa integrazione usando un binding di output che scrive i dati di una richiesta HTTP in un messaggio della coda. Il completamento di questo articolo non comporta costi aggiuntivi oltre ai pochi centesimi di dollaro USA dell'argomento di avvio rapido precedente. Per altre informazioni sui binding, vedere Concetti su trigger e binding di Funzioni di Azure.

Configurare l'ambiente locale

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Prima di iniziare, è necessario completare l'articolo Avvio rapido: Creare un progetto Funzioni di Azure dalla riga di comando. Se è già stata eseguita la pulizia delle risorse alla fine di tale articolo, eseguire di nuovo i passaggi per ricreare l'app per le funzioni e le risorse correlate in Azure.

Recuperare la stringa di connessione di Archiviazione di Azure

In precedenza è stato creato un account Archiviazione di Azure per l'uso dell'app per le funzioni. La stringa di connessione per questo account è archiviata in modo sicuro nelle impostazioni dell'app in Azure. Scaricando l'impostazione nel file di local.settings.json, è possibile usare la connessione per scrivere in una coda di Archiviazione nello stesso account quando si esegue la funzione in locale.

  1. Dalla radice del progetto eseguire il comando seguente, sostituire <APP_NAME> con il nome dell'app per le funzioni del passaggio precedente. Questo comando sovrascrive tutti i valori esistenti nel file.

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. Aprire local.settings.json file e individuare il valore denominato AzureWebJobsStorage, ovvero l'account Archiviazione stringa di connessione. Il nome AzureWebJobsStorage e la stringa di connessione si usano in altre sezioni di questo articolo.

Importante

Poiché il file local.settings.json contiene segreti scaricati da Azure, escludere sempre questo file dal controllo del codice sorgente. Il file con estensione gitignore creato con un progetto di funzioni locale esclude il file per impostazione predefinita.

Registrare le estensioni delle associazioni

Ad eccezione dei trigger HTTP e timer, i binding vengono implementati come pacchetti di estensione. Eseguire il comando dotnet add package seguente nella finestra del terminale per aggiungere il pacchetto di estensione di archiviazione nel progetto.

dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease

Ora è possibile aggiungere il binding di output di archiviazione nel progetto.

Aggiungere una definizione di binding di output alla funzione

Anche se una funzione può avere un solo trigger, può avere più associazioni di input e output, che consente di connettersi ad altri servizi e risorse di Azure senza scrivere codice di integrazione personalizzato.

Quando si usa il modello di programmazione Node.js v4, gli attributi di associazione vengono definiti direttamente nel file ./src/functions/HttpExample.js . Nell'argomento di avvio rapido precedente il file contiene già un'associazione HTTP definita dal app.http metodo .

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

app.http('httpTrigger', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: async (request, context) => {
    try {
      context.log(`Http function processed request for url "${request.url}"`);

      const name = request.query.get('name') || (await request.text());
      context.log(`Name: ${name}`);

      if (!name) {
        return { status: 404, body: 'Not Found' };
      }

      return { body: `Hello, ${name}!` };
    } catch (error) {
      context.log(`Error: ${error}`);
      return { status: 500, body: 'Internal Server Error' };
    }
  },
});

Quando si usa il modello di programmazione Node.js v4, gli attributi di associazione vengono definiti direttamente nel file ./src/functions/HttpExample.js . Nell'argomento di avvio rapido precedente il file contiene già un'associazione HTTP definita dal app.http metodo .

import {
  app,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
} from '@azure/functions';

export async function httpTrigger1(
  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('httpTrigger1', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: httpTrigger1,
});

Questi binding vengono dichiarati nel file function.json nella cartella della funzione. Dall'argomento di avvio rapido precedente il file function.json nella cartella HttpExample contiene due binding nella raccolta bindings:

Quando si usa il modello di programmazione Python v2, gli attributi di associazione vengono definiti direttamente nel file function_app.py come elementi decorator. Nell'argomento di avvio rapido precedente il file di function_app.py contiene già un'associazione basata su decorator:

import azure.functions as func
import logging

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="hello", auth_level=func.AuthLevel.ANONYMOUS)

L'elemento decorator aggiunge l'associazione route HttpTrigger e HttpOutput alla funzione, che consente l'attivazione della funzione quando le richieste HTTP raggiungono la route specificata.

Per scrivere in una coda Archiviazione di Azure da questa funzione, aggiungere l'elemento queue_output Decorator al codice della funzione:

@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")

Nell'elemento decorator arg_name identifica il parametro di associazione a cui si fa riferimento nel codice, queue_name è il nome della coda in cui scrive l'associazione e connection è il nome di un'impostazione dell'applicazione che contiene il stringa di connessione per l'account Archiviazione. Negli argomenti di avvio rapido si usa lo stesso account di archiviazione dell'app per le funzioni, che si trova nell'impostazione AzureWebJobsStorage (da local.settings.json file). Se queue_name non esiste, il binding lo crea al primo utilizzo.

"bindings": [
  {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "Request",
    "methods": [
      "get",
      "post"
    ]
  },
  {
    "type": "http",
    "direction": "out",
    "name": "Response"
  }
]

Per scrivere in una coda Archiviazione di Azure:

  • Aggiungere una extraOutputs proprietà alla configurazione dell'associazione

    {
        methods: ['GET', 'POST'],
        extraOutputs: [sendToQueue], // add output binding to HTTP trigger
        authLevel: 'anonymous',
        handler: () => {}
    }
    
  • Aggiungere una output.storageQueue funzione sopra la app.http chiamata

    const sendToQueue: StorageQueueOutput = output.storageQueue({
      queueName: 'outqueue',
      connection: 'AzureWebJobsStorage',
    });
    

Il secondo binding nella raccolta è denominato res. Questo binding http è un binding di output (out) usato per scrivere la risposta HTTP.

Per scrivere in una coda di Archiviazione di Azure da questa funzione, aggiungere un binding out di tipo queue con il nome msg, come illustrato nel codice seguente:

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

Per un queue tipo, è necessario specificare il nome della coda in queueName e specificare il nome della connessione Archiviazione di Azure (da local.settings.json file) in connection.

In un progetto C# le associazioni vengono definite come attributi di associazione nel metodo della funzione. Definizioni specifiche dipendono dal fatto che l'app venga eseguita in-process (libreria di classi C#) o in un processo di lavoro isolato.

Aprire il file di progetto HttpExample.cs e aggiungere la classe seguente MultiResponse :

public class MultiResponse
{
    [QueueOutput("outqueue",Connection = "AzureWebJobsStorage")]
    public string[] Messages { get; set; }
    public HttpResponseData HttpResponse { get; set; }
}

La MultiResponse classe consente di scrivere in una coda di archiviazione denominata outqueue e in un messaggio HTTP riuscito. È possibile inviare più messaggi alla coda perché l'attributo QueueOutput viene applicato a una matrice di stringhe.

La Connection proprietà imposta il stringa di connessione per l'account di archiviazione. In questo caso, è possibile omettere Connection, perché si usa già l'account di archiviazione predefinito.

In un progetto Java i binding vengono definiti come annotazioni di binding nel metodo della funzione. Il file function.json viene quindi generato automaticamente in base a queste annotazioni.

Passare al percorso del codice della funzione, src/main/java, aprire il file di progetto Function.java e aggiungere il parametro seguente alla definizione del metodo run:

@QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") OutputBinding<String> msg

Il msg parametro è un OutputBinding<T> tipo, che rappresenta una raccolta di stringhe. Queste stringhe vengono scritte come messaggi in un'associazione di output al termine della funzione. In questo caso, l'output è una coda di archiviazione denominata outqueue. La stringa di connessione per l'account di archiviazione è impostata dal metodo connection. Passare l'impostazione dell'applicazione che contiene il stringa di connessione dell'account Archiviazione anziché passare il stringa di connessione stesso.

La definizione del run metodo deve ora essere simile all'esempio seguente:

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION)  
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") 
        OutputBinding<String> msg, final ExecutionContext context) {
    ...
}

Per altre informazioni sui dettagli dei binding, vedere Concetti su trigger e binding di Funzioni di Azure e la configurazione dell'output della coda.

Aggiungere il codice per usare il binding di output

Una volta definita l'associazione della coda, è possibile aggiornare la funzione per ricevere il parametro di output msg e scrivere i messaggi nella coda.

Aggiornare HttpExample\function_app.py in modo che corrisponda al codice seguente, aggiungere il msg parametro alla definizione della funzione e msg.set(name) sotto l'istruzione if name: :

import azure.functions as func
import logging

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.route(route="HttpExample")
@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")
def HttpExample(req: func.HttpRequest, msg: func.Out [func.QueueMessage]) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        msg.set(name)
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

Il parametro msg è un'istanza della azure.functions.Out class. Il set metodo scrive un messaggio stringa nella coda. In questo caso, viene name passato alla funzione nella stringa di query URL.

Aggiungere il codice che usa l'oggetto di associazione di output su context.extraOutputs per creare un messaggio della coda. Aggiungere questo codice prima dell'istruzione return.

context.extraOutputs.set(sendToQueue, [msg]);

A questo punto, la funzione potrebbe essere simile alla seguente:

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

const sendToQueue = output.storageQueue({
  queueName: 'outqueue',
  connection: 'AzureWebJobsStorage',
});

app.http('HttpExample', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  extraOutputs: [sendToQueue],
  handler: async (request, context) => {
    try {
      context.log(`Http function processed request for url "${request.url}"`);

      const name = request.query.get('name') || (await request.text());
      context.log(`Name: ${name}`);

      if (name) {
        const msg = `Name passed to the function ${name}`;
        context.extraOutputs.set(sendToQueue, [msg]);
        return { body: msg };
      } else {
        context.log('Missing required data');
        return { status: 404, body: 'Missing required data' };
      }
    } catch (error) {
      context.log(`Error: ${error}`);
      return { status: 500, body: 'Internal Server Error' };
    }
  },
});

Aggiungere il codice che usa l'oggetto di associazione di output su context.extraOutputs per creare un messaggio della coda. Aggiungere questo codice prima dell'istruzione return.

context.extraOutputs.set(sendToQueue, [msg]);

A questo punto, la funzione potrebbe essere simile alla seguente:

import {
  app,
  output,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
  StorageQueueOutput,
} from '@azure/functions';

const sendToQueue: StorageQueueOutput = output.storageQueue({
  queueName: 'outqueue',
  connection: 'AzureWebJobsStorage',
});

export async function HttpExample(
  request: HttpRequest,
  context: InvocationContext,
): Promise<HttpResponseInit> {
  try {
    context.log(`Http function processed request for url "${request.url}"`);

    const name = request.query.get('name') || (await request.text());
    context.log(`Name: ${name}`);

    if (name) {
      const msg = `Name passed to the function ${name}`;
      context.extraOutputs.set(sendToQueue, [msg]);
      return { body: msg };
    } else {
      context.log('Missing required data');
      return { status: 404, body: 'Missing required data' };
    }
  } catch (error) {
    context.log(`Error: ${error}`);
    return { status: 500, body: 'Internal Server Error' };
  }
}

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

Aggiungere il codice che usa il cmdlet Push-OutputBinding per scrivere un testo nella coda usando il binding di output msg. Aggiungere questo codice prima di impostare lo stato OK nell'istruzione if.

$outputMsg = $name
Push-OutputBinding -name msg -Value $outputMsg

A questo punto, la funzione deve essere simile alla seguente:

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
if (-not $name) {
    $name = $Request.Body.Name
}

if ($name) {
    # Write the $name value to the queue, 
    # which is the name passed to the function.
    $outputMsg = $name
    Push-OutputBinding -name msg -Value $outputMsg

    $status = [HttpStatusCode]::OK
    $body = "Hello $name"
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please pass a name on the query string or in the request body."
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})

Sostituire la classe esistente HttpExample con il codice seguente:

    [Function("HttpExample")]
    public static MultiResponse Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
        FunctionContext executionContext)
    {
        var logger = executionContext.GetLogger("HttpExample");
        logger.LogInformation("C# HTTP trigger function processed a request.");

        var message = "Welcome to Azure Functions!";

        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
        response.WriteString(message);

        // Return a response to both HTTP trigger and storage output binding.
        return new MultiResponse()
        {
            // Write a single message.
            Messages = new string[] { message },
            HttpResponse = response
        };
    }
}

Ora è possibile usare il nuovo parametro msg per scrivere nel binding di output dal codice della funzione. Aggiungere la riga di codice seguente prima della risposta di operazione riuscita per aggiungere il valore di name al binding di output msg.

msg.setValue(name);

Quando si usa un binding di output, non è necessario usare il codice di Azure Storage SDK per l'autenticazione, per recuperare un riferimento alla coda o per scrivere dati. Queste attività vengono eseguite automaticamente dal runtime di Funzioni e dal binding di output.

Il run metodo deve ora essere simile all'esempio seguente:

public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) 
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", 
        connection = "AzureWebJobsStorage") OutputBinding<String> msg, 
        final ExecutionContext context) {
    context.getLogger().info("Java HTTP trigger processed a request.");

    // Parse query parameter
    String query = request.getQueryParameters().get("name");
    String name = request.getBody().orElse(query);

    if (name == null) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
        .body("Please pass a name on the query string or in the request body").build();
    } else {
        // Write the name to the message queue. 
        msg.setValue(name);

        return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
    }
}

Aggiornare i test

Poiché l'archetipo crea anche un set di test, è necessario aggiornare questi test per gestire il nuovo parametro msg nella firma del metodo run.

Passare al percorso del codice di test in src/test/java, aprire il file di progetto Function.java e sostituire la riga di codice in //Invoke con il codice seguente:

@SuppressWarnings("unchecked")
final OutputBinding<String> msg = (OutputBinding<String>)mock(OutputBinding.class);
final HttpResponseMessage ret = new Function().run(req, msg, context);

Si noti che non è necessario scrivere codice per l'autenticazione, ottenendo un riferimento alla coda o scrivendo dati. Tutte queste attività di integrazione vengono gestite facilmente nel runtime di Funzioni di Azure e nel binding di output della coda.

Eseguire la funzione in locale

  1. Eseguire la funzione avviando l'host di runtime Funzioni di Azure locale dalla cartella LocalFunctionProj.

    func start
    

    Verso la fine dell'output, devono essere visualizzate le righe seguenti:

    Screenshot dell'output della finestra del terminale durante l'esecuzione della funzione in locale.

    Nota

    Se HttpExample non viene visualizzato come illustrato sopra, è probabile che l'host non sia stato avviato dalla cartella radice del progetto. In tal caso, usare CTRL+C per arrestare l'host, passare alla cartella radice del progetto ed eseguire di nuovo il comando precedente.

  2. Copiare l'URL della funzione HTTP da questo output in un browser e aggiungere la stringa ?name=<YOUR_NAME>di query , rendendo l'URL completo, ad esempio http://localhost:7071/api/HttpExample?name=Functions. Il browser dovrebbe visualizzare un messaggio di risposta che restituisce il valore della stringa di query. Il terminale in cui è stato avviato il progetto mostra anche l'output del log quando si effettuano le richieste.

  3. Al termine, premere CTRL+C e digitare y per arrestare l'host delle funzioni.

Suggerimento

Durante l'avvio, l'host scarica e installa l'estensione di binding di archiviazione e altre estensioni di binding Microsoft. Questa installazione si verifica perché le estensioni di binding vengono abilitate per impostazione predefinita nel file host.json con le proprietà seguenti:

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}

Se si verificano errori relativi alle estensioni di binding, controllare che le proprietà sopra indicate siano presenti in host.json.

Visualizzare il messaggio nella coda di Archiviazione di Azure

È possibile visualizzare la coda nel portale di Azure o in Microsoft Azure Storage Explorer. È anche possibile visualizzare la coda nell'interfaccia della riga di comando di Azure, come descritto nei passaggi seguenti:

  1. Aprire il file local.setting.json del progetto di funzione e copiare il valore della stringa di connessione. In un terminale o in una finestra di comando eseguire il comando seguente per creare una variabile di ambiente denominata AZURE_STORAGE_CONNECTION_STRINGe incollare il stringa di connessione specifico al posto di <MY_CONNECTION_STRING>. Questa variabile di ambiente implica che non è necessario fornire la stringa di connessione a ogni comando successivo usando l'argomento --connection-string.

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (Facoltativo) Usare il comando az storage queue list per visualizzare le code di archiviazione dell'account. L'output di questo comando deve includere una coda denominata outqueue, creata quando la funzione ha scritto il primo messaggio in tale coda.

    az storage queue list --output tsv
    
  3. Usare il az storage message get comando per leggere il messaggio da questa coda, che deve essere il valore specificato durante il test della funzione in precedenza. Il comando legge e rimuove il primo messaggio dalla coda.

    echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
    

    Poiché il corpo del messaggio viene archiviato con codifica base64, il messaggio deve essere decodificato prima di essere visualizzato. Dopo l'esecuzione di az storage message get, il messaggio viene rimosso dalla coda. Se è presente un solo messaggio in outqueue, quando si esegue questo comando una seconda volta non viene recuperato alcun messaggio e si riceve invece un errore.

Ridistribuire il progetto in Azure

Dopo aver verificato in locale che la funzione ha scritto un messaggio nella coda di Archiviazione di Azure, è possibile ridistribuire il progetto per aggiornare l'endpoint in esecuzione in Azure.

Nella cartella LocalFunctionsProj usare il comando func azure functionapp publish per ridistribuire il progetto, sostituendo <APP_NAME> con il nome dell'app.

func azure functionapp publish <APP_NAME>

Nella cartella del progetto locale usare il comando Maven seguente per ripubblicare il progetto:

mvn azure-functions:deploy

Eseguire la verifica in Azure

  1. Come nell'argomento di avvio rapido precedente, usare un browser o un CURL per testare la funzione ridistribuita.

    Copiare l'URL di richiamo completo visualizzato nell'output del comando publish nella barra degli indirizzi di un browser, aggiungendo il parametro di query &name=Functions. Il browser dovrebbe visualizzare lo stesso output di quando è stata eseguita la funzione in locale.

  2. Esaminare di nuovo la coda di archiviazione, come descritto nella sezione precedente, per verificare che contenga il nuovo messaggio scritto nella coda.

Pulire le risorse

Al termine, usare il comando seguente per eliminare il gruppo di risorse e tutte le risorse contenute per evitare in incorrere in ulteriori costi.

az group delete --name AzureFunctionsQuickstart-rg

Passaggi successivi

La funzione di trigger HTTP è stata aggiornata per scrivere dati in una coda di archiviazione. Ora è possibile ottenere altre informazioni sullo sviluppo di Funzioni dalla riga di comando usando Core Tools e l'interfaccia della riga di comando di Azure: