Conectar o Azure Functions ao armazenamento do Azure usando ferramentas de linha de comando

Neste artigo, você integra uma fila do Armazenamento do Azure à função e à conta de armazenamento criada no artigo de início rápido anterior. Obtenha essa integração usando uma associação de saída que grava os dados de uma solicitação HTTP em uma mensagem na fila. A conclusão deste artigo não gera nenhum custo adicional além dos primeiros centavos em US$ do início rápido anterior. Para saber mais sobre associações, confira Conceitos de gatilhos e de associações do Azure Functions.

Observação

Atualmente, este artigo oferece suporte apenas a Node.js v3 para Functions.

Configurar o ambiente local

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Antes de começar, conclua o artigo Início rápido: criar um projeto de Azure Functions da linha de comando. Se você já limpou os recursos ao final daquele artigo, percorra as etapas novamente para recriar o aplicativo de funções e recursos relacionados no Azure.

Recuperar a cadeia de conexão do Armazenamento do Azure

Anteriormente, você criou uma conta de Armazenamento do Azure para o aplicativo de funções usar. A cadeia de conexão dessa conta é armazenada com segurança nas configurações do aplicativo no Azure. Ao baixar a configuração para o arquivo local.settings.json, use a conexão para gravar em uma fila do Armazenamento na mesma conta ao executar a função localmente.

  1. Na raiz do projeto, execute o comando a seguir, substituindo <APP_NAME> pelo nome do aplicativo de funções da etapa anterior. Esse comando substitui todos os valores existentes no arquivo.

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. Abra o arquivo local.settings.json e localize o valor chamado AzureWebJobsStorage, que é a cadeia de conexão da conta de armazenamento. Você usará o nome AzureWebJobsStorage e a cadeia de conexão em outras seções deste artigo.

Importante

Como o arquivo local.settings.json contém segredos baixados do Azure, sempre exclua esse arquivo do controle do código-fonte. O arquivo .gitignore criado com um projeto de funções locais exclui o arquivo por padrão.

Registrar as extensões de associação

Com exceção dos gatilhos de timer e HTTP, as associações são implementadas como pacotes de extensão. Execute o comando dotnet add package a seguir na janela Terminal para adicionar o pacote de extensão Armazenamento ao seu projeto.

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

Agora, você pode adicionar a associação de saída do armazenamento ao seu projeto.

Adicionar uma definição de associação de saída à função

Embora uma função possa ter apenas um gatilho, ela pode ter várias associações de entrada e saída, o que permite que você se conecte a outros serviços e recursos do Azure sem escrever um código de integração personalizado.

Você declara essas associações no arquivo function.json em sua pasta de funções. No início rápido anterior, o arquivo function.json na pasta HttpExample contém duas associações na coleção bindings:

Ao usar o modelo de programação Python v2, os atributos de ligação são definidos diretamente no arquivo function_app.py como decoradores. No início rápido anterior, o arquivo function_app.py já contém uma associação baseada em decorador:

import azure.functions as func
import logging

app = func.FunctionApp()

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

O decorador route adiciona a associação HttpTrigger e HttpOutput à função, o que permite que sua função seja disparada quando as solicitações http atingirem a rota especificada.

Para gravar em uma fila do Armazenamento do Azure a partir dessa função, adicione o decorador queue_output ao código de função:

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

Neste código, arg_name identifica o parâmetro de associação referenciado no seu código, queue_name é o nome da fila na qual a associação é gravada e connection é o nome de uma configuração de aplicativo que contém a cadeia de conexão da Conta de Armazenamento. No início rápido, você usa a mesma conta de armazenamento que o aplicativo de função, que está na configuração AzureWebJobsStorage (a partir do arquivo local.settings.json). Quando o queue_name não existe, a associação o cria no primeiro uso.

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

A segunda associação na coleção é denominada res. Essa associação http é uma associação de saída (out) usada para gravar a resposta HTTP.

Para fazer uma gravação em uma fila do Armazenamento do Azure por meio dessa função, adicione uma associação out do tipo queue com o nome msg, conforme mostrado no código abaixo:

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

A segunda associação na coleção é denominada res. Essa associação http é uma associação de saída (out) usada para gravar a resposta HTTP.

Para fazer uma gravação em uma fila do Armazenamento do Azure por meio dessa função, adicione uma associação out do tipo queue com o nome msg, conforme mostrado no código abaixo:

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

Nesse caso, msg é fornecido à função como um argumento de saída. Para um tipo queue, também é necessário especificar o nome da fila em queueName e fornecer o nome da conexão do Armazenamento do Azure (por meio do arquivo local.settings.json) em connection.

Em um projeto C#, as associações são definidas como atributos de associação no método de função. Definições específicas dependem de o aplicativo ser executado em processo (biblioteca de classes C#) ou em um processo de trabalho isolado.

Abra o arquivo de projeto HttpExample.cs e adicione a seguinte classe MultiResponse:

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

A classe MultiResponse permite que você faça gravações em uma fila de armazenamento chamada outqueue e grave uma mensagem de êxito HTTP. Várias mensagens podem ser enviadas para a fila, porque o atributo QueueOutput é aplicado a uma matriz de cadeia de caracteres.

A propriedade Connection define a cadeia de conexão da conta de armazenamento. Nesse caso, você poderia omitir Connection porque já está usando a conta de armazenamento padrão.

Em um projeto Java, as associações são definidas como anotações de associação no método de função. O arquivo function.json é então gerado automaticamente com base nessas anotações.

Procure a localização do código de função em src/main/java, abra o arquivo de projeto Function.java e adicione o seguinte parâmetro à definição do método run:

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

O parâmetro msg é um tipo OutputBinding<T>, que representa uma coleção de cadeias de caracteres. Essas cadeias de caracteres são gravadas como mensagens em uma associação de saída quando a função é concluída. Nesse caso, a saída é uma fila de armazenamento denominada outqueue. A cadeia de conexão para a conta de armazenamento é definida pelo método connection. Passe a configuração de aplicativo que contém a cadeia de conexão da conta de armazenamento, em vez de passar a própria cadeia de conexão.

A definição do método run agora deverá ser semelhante ao seguinte exemplo:

@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) {
    ...
}

Para obter mais informações sobre os detalhes das associações, confira Conceitos de associações e gatilhos do Azure Functions e configuração de saída da fila.

Adicionar código para usar a associação de saída

Com a associação de fila definida, agora você pode atualizar sua função para receber o parâmetro de saída msg e gravar mensagens na fila.

Atualize HttpExample\__init__.py para que corresponda ao seguinte código, adicionando o parâmetro msg à definição de função e msg.set(name) abaixo da instrução 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
        )

O parâmetro msg é uma instância do azure.functions.Out class. O método set grava uma mensagem de cadeia de caracteres na fila. Nesse caso, é o name passado para a função na cadeia de caracteres de consulta de URL.

Adicione um código que usa o objeto de associação de saída msg em context.bindings para criar uma mensagem da fila. Adicione esse código antes da instrução context.res.

// Add a message to the Storage queue,
// which is the name passed to the function.
context.bindings.msg = (req.query.name || req.body.name);

Nesse momento, sua função deve ser a seguinte:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    if (req.query.name || (req.body && req.body.name)) {
        // Add a message to the Storage queue,
        // which is the name passed to the function.
        context.bindings.msg = (req.query.name || req.body.name);
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

Adicione um código que usa o objeto de associação de saída msg em context.bindings para criar uma mensagem da fila. Adicione esse código antes da instrução context.res.

context.bindings.msg = name;

Neste ponto, sua função deve ser a seguinte:

import { AzureFunction, Context, HttpRequest } from "@azure/functions"

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    const name = (req.query.name || (req.body && req.body.name));

    if (name) {
        // Add a message to the storage queue, 
        // which is the name passed to the function.
        context.bindings.msg = name; 
        // Send a "hello" response.
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

export default httpTrigger;

Adicione código que usa o cmdlet Push-OutputBinding para gravar texto na fila usando a associação de saída msg. Adicione esse código antes de definir o status como OK na instrução if.

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

Neste ponto, sua função deve ser a seguinte:

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

Substitua a classe HttpExample existente pelo seguinte código:

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

Agora, você pode usar o novo parâmetro msg para fazer a gravação na associação de saída por meio do código de função. Adicione a linha de código a seguir antes da resposta de êxito para adicionar o valor de name à associação de saída msg.

msg.setValue(name);

Ao usar uma associação de saída, não é necessário usar o código do SDK do Armazenamento do Azure para se autenticar, para obter uma referência de fila ou para escrever dados. O runtime do Functions e a associação de saída da fila fazem essas tarefas para você.

O método run agora deverá ser semelhante ao seguinte exemplo:

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

Atualizar os testes

Como o arquétipo também cria um conjunto de testes, você precisa atualizar esses testes para manipular o novo parâmetro msg na assinatura do método run.

Procure a localização do código de teste em src/test/java, abra o arquivo de projeto Function.java e substitua a linha de código em //Invoke pelo código a seguir:

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

Observe que você não precisa escrever nenhum código para autenticação, obtenção de uma referência de fila ou gravação de dados. Todas essas tarefas de integração são convenientemente processadas no Azure Functions Runtime e na associação de saída da fila.

Executar a função localmente

  1. Execute a função iniciando o host de runtime local do Azure Functions na pasta LocalFunctionProj.

    func start
    

    Perto do fim da saída, as seguintes linhas precisam ser exibidas:

    Captura de tela da saída da janela do terminal ao executar a função localmente.

    Observação

    Se HttpExample não aparece conforme mostrado acima, é provável que você tenha iniciado o host fora da pasta raiz do projeto. Nesse caso, use CTRL+C para interromper o host, procure a pasta raiz do projeto e execute o comando anterior novamente.

  2. Copie a URL da função HTTP dessa saída para um navegador e acrescente a cadeia de caracteres de consulta ?name=<YOUR_NAME>, fazendo com que a URL completa seja http://localhost:7071/api/HttpExample?name=Functions. O navegador exibirá uma mensagem de resposta que retorna o valor da cadeia de consulta. O terminal em que você iniciou seu projeto também mostra a saída do log conforme você faz solicitações.

  3. Quando terminar, pressione CTRL + C e digite y para interromper o host de funções.

Dica

Durante a inicialização, o host baixa e instala a Extensão de associação de armazenamento e outras extensões de associação da Microsoft. Essa instalação ocorre porque as extensões de associação estão habilitadas por padrão no arquivo host.json com as seguintes propriedades:

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

Se você receber erros relacionados às extensões de associação, verifique se as propriedades acima estão presentes em host.json.

Exibir a mensagem na fila do Armazenamento do Azure

Veja a fila no portal do Azure ou no Gerenciador de Armazenamento do Microsoft Azure. Veja também a fila na CLI do Azure, conforme descrito nas seguintes etapas:

  1. Abra o arquivo local.setting.json do projeto de funções e copie o valor da cadeia de conexão. Em um terminal ou uma janela Comando, execute o comando a seguir para criar uma variável de ambiente chamada AZURE_STORAGE_CONNECTION_STRING e cole a cadeia de conexão específica no lugar de <MY_CONNECTION_STRING>. (Essa variável de ambiente significa que você não precisa fornecer a cadeia de conexão para cada comando posterior usando o argumento --connection-string.)

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (Opcional) Use o comando az storage queue list para ver as filas de armazenamento em sua conta. A saída desse comando deve incluir uma fila chamada outqueue, que foi criada quando a função gravou a primeira mensagem nessa fila.

    az storage queue list --output tsv
    
  3. Use o comando az storage message get para ler as mensagens dessa fila, que deve ser o valor que você indicou ao testar a função anteriormente. O comando lê e remove a primeira mensagem da fila.

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

    Como o corpo da mensagem é armazenado codificado em base64, a mensagem deve ser decodificada antes de ser exibida. Depois de executar az storage message get, a mensagem é removida da fila. Se houver apenas uma mensagem em outqueue, você não recuperará uma mensagem quando executar esse comando pela segunda vez e receberá um erro.

Reimplantar o projeto no Azure

Agora que você verificou localmente que a função gravou uma mensagem na fila do Armazenamento do Azure, poderá reimplantar o projeto para atualizar o ponto de extremidade em execução no Azure.

Na pasta LocalFunctionsProj, use o comando func azure functionapp publish para reimplantar o projeto, substituindo<APP_NAME> pelo nome do aplicativo.

func azure functionapp publish <APP_NAME>

Na pasta do projeto local, use o seguinte comando do Maven para republicar seu projeto:

mvn azure-functions:deploy

Verificar no Azure

  1. Como no início rápido anterior, use um navegador ou um cURL para testar a função reimplantada.

    Copie a URL de Invocação completa mostrada na saída do comando de publicação na barra de endereços de um navegador, acrescentando o parâmetro de consulta &name=Functions. O navegador deverá exibir uma saída semelhante à que foi exibida quando você executou a função localmente.

  2. Examine a fila do Armazenamento novamente, conforme descrito na seção anterior, para verificar se ela contém a nova mensagem gravada na fila.

Limpar os recursos

Depois de terminar, use o comando a seguir para excluir o grupo de recursos e todos os recursos contidos nele para evitar custos adicionais.

az group delete --name AzureFunctionsQuickstart-rg

Próximas etapas

Você atualizou sua função disparada por HTTP para gravar dados em uma Fila de armazenamento. Agora você pode aprender mais sobre o desenvolvimento de funções na linha de comando usando o Core Tools e a CLI do Azure: