Conexión de Azure Functions a Azure Storage mediante herramientas de línea de comandos
En este artículo, integrará una cola de Azure Storage con la función y la cuenta de almacenamiento que creó en el artículo de inicio rápido anterior. Para lograr esta integración se usa un enlace de salida que escribe los datos de una solicitud HTTP en un mensaje de la cola. Completar este artículo no supone ningún costo adicional sobre el pequeño importe del inicio rápido anterior. Para más información acerca de los enlaces, consulte Conceptos básicos sobre los enlaces y desencadenadores de Azure Functions.
Configuración del entorno local
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Antes de empezar, debe completar el artículo Inicio rápido: Creación de un proyecto de Azure Functions desde la línea de comandos. Si ya ha limpiado los recursos al final de ese artículo, vuelva a recorrer los pasos para crear de nuevo la aplicación de función y los recursos relacionados en Azure.
Recuperación de la cadena de conexión de Azure Storage
Anteriormente, creó una cuenta de Azure Storage para el uso de la aplicación de funciones. La cadena de conexión de esta cuenta se almacena de forma segura en la configuración de la aplicación en Azure. Mediante la descarga de la configuración en el archivo local.settings.json, puede usar la conexión para escribir en una cola de Storage de la misma cuenta cuando ejecute la función de forma local.
En la raíz del proyecto, ejecute el comando siguiente, reemplace
<APP_NAME>
por el nombre de la aplicación de funciones del paso anterior. Este comando sobrescribe los valores existentes en el archivo.func azure functionapp fetch-app-settings <APP_NAME>
Abra el archivo local.settings.json y busque el valor denominado
AzureWebJobsStorage
, que es la cadena de conexión de la cuenta de almacenamiento. Usará el nombreAzureWebJobsStorage
y la cadena de conexión en otras secciones de este artículo.
Importante
Como el archivo local.settings.json contiene secretos descargados de Azure, excluya siempre este archivo del control de código fuente. El archivo .gitignore que se creó con un proyecto de Functions local excluye el archivo de forma predeterminada.
Registro de extensiones de enlace
Excepto los desencadenadores HTTP y del temporizador, los enlaces se implementan como paquetes de extensión. Ejecute el siguiente comando dotnet add package en la ventana Terminal para agregar el paquete de extensión de Storage al proyecto.
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease
Ahora podrá agregar el enlace de salida de almacenamiento al proyecto.
Incorporación de una definición de enlace de salida a la función
Aunque ninguna de las funciones puede tener más de un desencadenador, puede tener varios enlaces de entrada y salida que le permiten conectarse a otros servicios y recursos de Azure sin escribir código de integración personalizado.
Al usar el modelo de programación de Node.js v4, los atributos de enlace se definen directamente en el archivo ./src/functions/HttpExample.js. En el inicio rápido anterior, el archivo ya contiene un enlace HTTP definido por el app.http
método.
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' };
}
},
});
Al usar el modelo de programación de Node.js v4, los atributos de enlace se definen directamente en el archivo ./src/functions/HttpExample.js. En el inicio rápido anterior, el archivo ya contiene un enlace HTTP definido por el app.http
método.
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,
});
Estos enlaces se declaran en el archivo function.json en la carpeta de la función. En el inicio rápido anterior, el archivo function.json de la carpeta HttpExample contiene dos enlaces en la colección bindings
:
Si se usa el modelo de programación de Python v2, los atributos de enlace se definen directamente en el archivo function_app.py como decoradores. En el inicio rápido anterior, el archivo function_app.py ya contiene un enlace basado en 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)
El decorador route
agrega el enlace HttpTrigger y HttpOutput a la función, lo que permite que la función se desencadene cuando las solicitudes http alcancen la ruta especificada.
Para escribir en una cola de Azure Storage desde esta función, agregue el decorador queue_output
al código de función:
@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")
En el decorador, arg_name
identifica el parámetro de enlace al que se hace referencia en el código, queue_name
es el nombre de la cola en la que escribe el enlace y connection
es el nombre de una configuración de la aplicación que contiene la cadena de conexión para la cuenta de Storage. En los inicios rápidos se usa la misma cuenta de almacenamiento que la aplicación de funciones, que se encuentra en la configuración AzureWebJobsStorage
(desde el archivo local.settings.json). Cuando no existe queue_name
, el enlace lo crea durante el primer uso.
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "Request",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "Response"
}
]
Para escribir en una cola de Azure Storage:
Agregar una propiedad
extraOutputs
a la configuración de enlace{ methods: ['GET', 'POST'], extraOutputs: [sendToQueue], // add output binding to HTTP trigger authLevel: 'anonymous', handler: () => {} }
Agregar una función
output.storageQueue
encima de la llamadaapp.http
const sendToQueue: StorageQueueOutput = output.storageQueue({ queueName: 'outqueue', connection: 'AzureWebJobsStorage', });
El segundo enlace de la colección se denomina res
. Este enlace http
es un enlace de salida (out
) que se usa para escribir la respuesta HTTP.
Para escribir en una cola de Azure Storage desde esta función, agregue un enlace out
del tipo queue
con el nombre msg
, como se muestra en el código siguiente:
{
"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"
}
]
}
En el caso de un tipo queue
, también debe especificar el nombre de la cola en queueName
y proporcionar el nombre de la conexión Azure Storage (desde el archivo local.settings.json) en connection
.
En un proyecto de C#, los enlaces se definen como atributos de enlace en el método de función. Las definiciones específicas dependen de si la aplicación se ejecuta en proceso (biblioteca de clases de C#) o en un proceso de trabajo aislado.
Abra el archivo de proyecto HttpExample.cs y agregue la siguiente clase MultiResponse
:
public class MultiResponse
{
[QueueOutput("outqueue",Connection = "AzureWebJobsStorage")]
public string[] Messages { get; set; }
public HttpResponseData HttpResponse { get; set; }
}
La clase MultiResponse
le permite escribir en una cola de almacenamiento denominada outqueue
y en un mensaje HTTP de operación correcta. Como el atributo QueueOutput
se aplica a una matriz de cadenas, se podrían enviar varios mensajes a la cola.
La propiedad Connection
le permite establecer una cadena de conexión para la cuenta de almacenamiento. En este caso, puede omitir Connection
, puesto que ya está usando la cuenta de almacenamiento predeterminada.
En un proyecto de Java, los enlaces se definen como anotaciones de enlace en el método de función. Entonces, el archivo function.json se genera automáticamente en función de estas anotaciones.
Vaya a la ubicación del código de función en src/main/java, abra el archivo de proyecto Function.java y agregue el parámetro siguiente a la definición del método run
:
@QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") OutputBinding<String> msg
El parámetro msg
es un tipo OutputBinding<T>
, que representa una colección de cadenas. Estas cadenas se escriben como mensajes en un enlace de salida cuando se completa la función. En este caso, la salida es una cola de almacenamiento denominada outqueue
. La cadena de conexión de la cuenta de Storage la establece el método connection
. Se pasa la configuración de la aplicación que contiene la cadena de conexión de la cuenta Storage, en lugar de pasar la propia cadena de conexión.
La definición del método run
debe ahora parecerse a la del siguiente ejemplo:
@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 más información sobre los enlaces, consulte Conceptos básicos sobre los enlaces y desencadenadores de Azure Functions y configuración de la cola de salida.
Adición de código para usar el enlace de salida
Con el enlace de cola definido, ahora puede actualizar la función para que reciba el parámetro de salida msg
y escribir mensajes en la cola.
Actualice HttpExample\function_app.py para que se ajuste al siguiente código y agregue el parámetro msg
a la definición de la función y msg.set(name)
en la instrucción 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
)
El parámetro msg
es una instancia de azure.functions.Out class
. El método set
escribe un mensaje de cadena en la cola. En este caso, es el name
que pasa a la función en la cadena de consulta de URL.
Agregue código que use el objeto de enlace de salida en context.extraOutputs
para crear un mensaje de cola. Agregue este código antes de la instrucción return.
context.extraOutputs.set(sendToQueue, [msg]);
En este momento, la función podría tener el aspecto siguiente:
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' };
}
},
});
Agregue código que use el objeto de enlace de salida en context.extraOutputs
para crear un mensaje de cola. Agregue este código antes de la instrucción return.
context.extraOutputs.set(sendToQueue, [msg]);
En este momento, la función podría tener el aspecto siguiente:
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,
});
Agregue código que use el cmdlet Push-OutputBinding
para escribir texto en la cola mediante el enlace de salida msg
. Agregue este código antes de establecer el estado correcto en la instrucción if
.
$outputMsg = $name
Push-OutputBinding -name msg -Value $outputMsg
En este momento, la función debe tener el aspecto siguiente:
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
})
Reemplace la clase HttpExample
existente por el código siguiente:
[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
};
}
}
Ahora, puede usar el nuevo parámetro msg
para escribir en el enlace de salida desde el código de la función. Agregue la siguiente línea de código antes de la respuesta de operación correcta para agregar el valor de name
al enlace de salida msg
.
msg.setValue(name);
Al usar un enlace de salida, no tiene que usar el código del SDK de Azure Storage para autenticarse, obtener una referencia de cola o escribir datos. El sistema en tiempo de ejecución de Functions y el enlace de salida de cola realizan esas tareas automáticamente.
El método run
debe ahora parecerse al del siguiente ejemplo:
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();
}
}
Actualización de las pruebas
Dado que el arquetipo también crea un conjunto de pruebas, debe actualizar estas pruebas para controlar el nuevo parámetro msg
en la signatura del método run
.
Vaya a la ubicación del código de prueba en src/test/java, abra el archivo de proyecto Function.Java y reemplace la línea de código debajo de //Invoke
por el código siguiente:
@SuppressWarnings("unchecked")
final OutputBinding<String> msg = (OutputBinding<String>)mock(OutputBinding.class);
final HttpResponseMessage ret = new Function().run(req, msg, context);
Observe que no necesita escribir código para la autenticación, para obtener una referencia de la cola ni para escribir datos. Todas estas tareas de integración se administran de manera adecuada en el entorno de ejecución de Azure Functions y en el enlace de salida de la cola.
Ejecución local de la función
Para ejecutar la función, inicie el host en tiempo de ejecución local de Azure Functions desde la carpeta LocalFunctionProj.
func start
En la parte final de la salida, deberán aparecer las líneas siguientes:
Nota
Si HttpExample no aparece como se ha mostrado arriba, es probable que haya iniciado el host desde fuera de la carpeta raíz del proyecto. En ese caso, use Ctrl+C para detener el host, vaya a la carpeta raíz del proyecto y vuelva a ejecutar el comando anterior.
Copie la URL de su función HTTP de esta salida a un navegador y agregue la cadena de consulta
?name=<YOUR_NAME>
, lo que hace que la URL completa sea comohttp://localhost:7071/api/HttpExample?name=Functions
. El explorador debe mostrar un mensaje de respuesta que devuelve el valor de la cadena de consulta. El terminal en el que inició el proyecto también muestra la salida del registro cuando realiza solicitudes.Cuando termine, presione Ctrl + C y escriba
y
para detener el host de Azure Functions.
Sugerencia
Durante el inicio, el host descarga e instala la extensión de enlace de Storage y otras extensiones de enlace de Microsoft. Esta instalación se produce porque las extensiones de enlace se habilitan de forma predeterminada en el archivo host.json con las siguientes propiedades:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}
Si encuentra algún error relacionado con las extensiones de enlace, compruebe que las propiedades anteriores están presentes en host.json.
Visualización del mensaje en la cola de Azure Storage
La cola se puede ver en Azure Portal o en el Explorador de Microsoft Azure Storage. También puede ver la cola en la CLI de Azure, como se describe en los pasos siguientes:
Abra el archivo local.setting.json del proyecto de Functions y copie el valor de la cadena de conexión. En una ventana de terminal o de comandos, ejecute el siguiente comando para crear una variable de entorno denominada
AZURE_STORAGE_CONNECTION_STRING
y pegue la cadena de conexión concreta en lugar de<MY_CONNECTION_STRING>
. (Esta variable de entorno significa que no es necesario proporcionar la cadena de conexión a cada comando posterior mediante el argumento--connection-string
).export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
(Opcional) Puede usar el comando
az storage queue list
para ver las colas de Storage de la cuenta. La salida de este comando debe incluir una cola denominadaoutqueue
, la cual se creó cuando la función escribió su primer mensaje en esa cola.az storage queue list --output tsv
Use el comando
az storage message get
para leer el mensaje de esta cola, que debería ser el valor que indicó al probar la función anteriormente. El comando lee y quita el primer mensaje de la cola.echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
Dado que el cuerpo del mensaje se almacena codificado en Base64, el mensaje se debe descodificar antes de visualizarse. Después de ejecutar
az storage message get
, el mensaje se quita de la cola. Si solo había un mensaje enoutqueue
, no se recuperará al ejecutar este comando una segunda vez y, en su lugar, recibirá un error.
Nueva implementación del proyecto en Azure
Ahora que ha comprobado localmente que la función escribió un mensaje en la cola de Azure Storage, puede volver a implementar el proyecto para actualizar el punto de conexión que se ejecuta en Azure.
En la carpeta LocalFunctionsProj, use el comando func azure functionapp publish
para volver a implementar el proyecto y reemplace<APP_NAME>
el nombre de la aplicación.
func azure functionapp publish <APP_NAME>
En la carpeta de proyecto local, use el siguiente comando de Maven para volver a publicar el proyecto:
mvn azure-functions:deploy
Comprobación en Azure
Como en el inicio rápido anterior, use un explorador o CURL para probar la función que ha vuelto a implementar.
Vuelva a examinar la cola de Storage, como se describe en la sección anterior, para comprobar que contiene el nuevo mensaje escrito en la cola.
Limpieza de recursos
Cuando haya terminado, use el siguiente comando para eliminar el grupo de recursos y todos los recursos que contiene para evitar incurrir en costos adicionales.
az group delete --name AzureFunctionsQuickstart-rg
Pasos siguientes
Ha actualizado la función desencadenada por HTTP para escribir datos en una cola de almacenamiento. Ahora puede aprender más sobre el desarrollo de Functions desde la línea de comandos mediante Core Tools y la CLI de Azure: