Tutoriales > Solution Designer > Flujo de trabajo > Personalización de flujos de trabajo en Therefore™ Online Desarrollar una nueva función de Azure |
Scroll |
¿Cómo se desarrolla una nueva función de Azure?
Si bien hay muchos lenguajes y herramientas para elegir, en esta guía solo se explica cómo crear funciones de Azure utilizando C# en Visual Studio. Esta es la forma recomendada, ya que las funciones de Azure se pueden comprobar y depurar localmente con solo pulsar F5 sin necesidad de una suscripción a Azure. También es posible desplegarlas en Azure desde Visual Studio.
Asegúrese de que tiene instalado Visual Studio con la carga de trabajo “desarrollo de Azure”.
En Visual Studio, haga clic en el menú “Herramientas” y, a continuación, “Get Tools and Features…” para abrir el instalador.
Abra la pestaña “Cargas de trabajo” y busque “Desarrollo de Azure”. Active la casilla de la parte superior y haga clic en «Modificar» para aplicar los cambios.
|
---|
¿Cómo se crea una nueva función de Azure?
Si la carga de trabajo «Desarrollo de Azure» se ha instalado correctamente, «Azure Functions» será una de las opciones para crear un nuevo proyecto en Visual Studio. Sin embargo, se recomienda comenzar desde el ejemplo “EmptyFunctionApp”, en lguar de iniciar el desarrollo desde cero. Asegúrese de seleccionar «Azure Functions v2» (ya que «v1» dejará de utilizarse en algún momento) y «Desencadenador Http», ya que Therefore™ solo admite actualmente llamadas a funciones de Azure mediante solicitudes HTTP(S). Los derechos de acceso solo se aplicarán cuando se publique la función, no para desarrollo local. Si selecciona «Anónimo», no se requerirá ninguna clave. «Función» requerirá la clave de la función, mientras que «Administrador» requerirá la clave de Function App. También puede utilizarse la clave maestra. |
---|
¿Cómo se utiliza REST para llamadas a la API web?
Añada al proyecto la carpeta “Therefore” del ejemplo “EmptyFunctionApp”.
Los archivos no son necesarios, pero facilitan y agilizan el desarrollo. Si los cambios en la API web requieren la actualización de WebAPIContract.cs, siga estos pasos:
1.Descargue e instale Silverlight 5.0 o superior 2.Asegúrese de que se está ejecutando el servicio XML 3.Cree una carpeta temporal, por ejemplo, “C:\WebAPI” 4.Abra una línea CMD y vaya a “C:\Program Files (x86)\Microsoft SDKs\Silverlight\v5.0\Tools” 5.Ejecute el siguiente comando: SlSvcUtil.exe http://servername:8000/theservice/v0001?singlewsdl /out: C:\WebAPI \WebAPIContract.cs /config: C:\WebAPI \output /namespace:http://schemas.therefore.net/webservices/interop/v0001/types,Therefore.WebAPI 6. Vaya a «C:\WebAPI» y copie WebAPIContract.cs en la carpeta «Therefore\WebAPI», sustituyendo el antiguo.
Como se describe en el proyecto de ejemplo, la función SendRequest de la clase Therefore.WebAPI.WebApiClient puede utilizarse para enviar una solicitud. Para una lista de comandos disponibles, consulte la documentación de la API web.
El uso de WebAPIContract.cs requerirá el paquete NuGet “System.ServiceModel.Http”. Consulte Dependencias para obtener información adicional.
|
---|
¿Cómo se utiliza SOAP para llamadas a la API web?
Añada al proyecto la carpeta “Therefore” del ejemplo “EmptyFunctionApp” y, a continuación, elimine la subcarpeta “WebAPI”, ya que solo se necesita para llamadas REST. Dado que no se recomienda el uso de SOAP, el valor de “UseREST” en la clase TheConfig debe establecerse como “false”. En caso contrario, la propiedad WebAPIBaseUrl será incorrecta.
Añada la API web de Therefore™ como Servicio conetado:
Como Servicio conectado, elija “Microsoft WCF Web Service Reference Provider”. Inserte el URL donde se ejecuta el servicio web y pulse “Ir”. Si se está ejecutando el servicio, deberá aparecer bajo Servicios como «ThereforeService».
Introduzca un espacio de nombres significativo como “Therefore.WebAPI” y pulse Finalizar.
La clase Therefore.WebAPI.ThereforeServiceClient puede utilizarse para realizar llamadas a la API Web.
Consulte Dependencias para obtener información adicional sobre el paquete Nuget «System.ServiceModel.Http».
|
---|
Para añadir el paquete NuGet “System.ServiceModel.Http”, haga clic con el botón derecho en el proyecto y seleccione “Administrar paquetes NuGet…”
Cambie a la pestaña Examinar y busque «System.ServiceModel.Http», selecciónelo y haga clic en Instalar.
Si recibe el siguiente error al compilar, aún no se ha resuelto un error:
System.IO.FileNotFoundException: No se puede cargar el archivo o el ensamblado 'System.Private.ServiceModel, Version=4.5.0.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. El sistema no encuentra el archivo especificado.
Como solución alternativa, edite el archivo .csproj y añada la siguiente configuración como secundaria del nodo raíz “Project”:
<!--esta es una solución temporal para el problema https://github.com/dotnet/wcf/issues/2824 y también requiere paquete nuget--> <Target Name="CopySPSM" BeforeTargets="Build"> <Copy SourceFiles="$(USERPROFILE)\.nuget\packages\system.private.servicemodel\ </Target> <ItemGroup> <None Include="$(USERPROFILE)\.nuget\packages\system.private.servicemodel\ </ItemGroup> <!--end workaround-->
Tenga en cuenta que quizá deba ajustar los números de versión y los saltos de línea. |
---|
El ejemplo EmptyFunctionApp es una pequeña función de Azure que recupera los parámetros pasados desde Therefore™, configura el objeto WebApiClient para que esté listo para utilizar y devuelve los parámetros requeridos cuando termina. Se ha diseñado para utilizar como punto de partida para desarrollo posterior.
public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "v1/Function1")] HttpRequest req, ILogger log) { string errMsg = ""; // error message that will be returned to Therefore for logging Exception logEx = null; // exception details for logging bool routingDone = false; // false: WF instance will be routed to the next task. // true: WF instance will not be routed because routing was done by WebAPI call try…
if (!String.IsNullOrWhiteSpace(errMsg) || logEx != null) log.LogError(logEx, errMsg); // log the error so it can be viewed in the Azure Portal
TheRespParams respParams = new TheRespParams(errMsg, routingDone); return new OkObjectResult(respParams.Serialize());
La función “Run” es la función principal a la que se llamará en una solicitud HTTP.
El «AuthorizationLevel» definido en el HttpTrigger solo se aplicará cuando se publique la función, no para desarrollo local. Si elige «Anónimo» no se requerirá ninguna clave, «Función» requerirá la clave de función y «Administrador» requerirá la clave Function App. También puede utilizarse la clave maestra.
La «Ruta» definid aen HttpTrigger como «v1/Function1», proporciona un modo de control de versiones. Supongamos que una nueva versión del flujo de trabajo requeriría una nueva versión de la función, pero algunas instancias deben permanecer en la antigua. La función podría copiarse y cambiarse la ruta de “v1” a “v2” en el código y en la configuración de nuevas tareas para dar respuesta a esta necesidad. Una Function App puede albergar múltiples funciones.
Al final del ejemplo, los errores que puedan haberse producido se registran en Azre y también se pasan a un objeto de clase “TheRespParams”. La clase “TheRespParams” se encarga de convertir los parámetros de devolución obligatorios para Therefore™ como objeto JSON.
Siempre se crea un objeto “OkObjectResult”, que devuelve el objeto JSON con un código de estado HTTP de 200. Todos los mensajes de error de “errMsg” se devolverán a Therefore™ para su registro en la historia de WF. Del lado de Therefore™, la llamada de función se ha realizado correctamente si no se ha producido un error (errMsg está vacío). |
---|
try { // leer parámetros pasados por Therefore string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); TheReqParams reqParams = TheReqParams.Deserialize(requestBody); if (reqParams == null || reqParams.TheInstanceNo <= 0 || reqParams.TheTokenNo <= 0) return new BadRequestObjectResult("Parámetro obligatorio no establecido"); // la función no fue devuelta por Therefore, se devuelve HTTP error 400 reqParams.LogWFInfo(log); // registrar información de flujo de trabajo ahora, en caso de que la función se bloquee luego
// configuración TheConfig config = TheConfig.Instance; config.WebAPIBaseUrl = reqParams.TheWebAPIUrl; config.Tenant = reqParams.TheTenant;
// conexión con la API web WebApiClient client; // especifique un nombre de usuario y una contraseña si no serían suficientes los permisos de accessToken if (!String.IsNullOrEmpty(config.UserName) && !String.IsNullOrEmpty(config.Password)) // conexión con el nombre de usuaro y contraseña/token: client = new WebApiClient(config.UserName, config.Password, config.IsToken, config.WebAPIUrl, config.Tenant, config.Language, config.RequestTimeout, config.ClientTimezoneIANA); else // conexión con token de portador JWT 'accessToken' client = new WebApiClient(reqParams.TheAccessToken, config.WebAPIUrl, config.Tenant, config.Language, config.RequestTimeout, config.ClientTimezoneIANA);
// Añada código aquí }
“TheReqParams” se encarga de leer todos los parámetros de solicitud pasados desde Therefore™.
La clase “TheConfig” tiene diversas propiedades, necesarias para la llamada a la API web, como idioma, zona horaria, tiempo de espera de solicitud y otras muchas. Estas se pueden configurar de acuerdo con sus necesidades o dejarse con los valores predeterminados.
Hay dos propiedades sin valores predeterminados útiles: “WebAPIBaseUrl” y “Tenant”. Los valores se pasan desde el servidor de Therefore™ y deben asignarse al objeto “TheConfig”.
La clase TheConfig leerá las propiedades UserName y Password desde “local.settings.json” cuando se realice desarrollo local o desde la configuración de la aplicación cuando se ejecute en Azure.
El objeto WebApiClient puede instanciarse utilizando credenciales de usuario o el JWT-token.
Esto se decide comprobando si están vacías o no. Si no se configuran credenciales, se utiliza el JWT-token.
La función SendRequest de la clase WebApiClient puede servir para enviar una solicitud. Para una lista de comandos disponibles, consulte la documentación de la API web.
En el comentario «Añada código aquí», puede realizar solicitudes a la API web e implementart la lógica de negocio.
Tanto si se produce un error como si el resultado es correcto, se devolverá el error o una cadena vacía para que Therefore™ sepa si la función se comporta de la forma deseada (fuera de la instrucción try/catch). |
---|
Esta documentación solo explica cómo enviar solicitudes utilizando la API web en general, pero no describe cada función disponible. Consulte la documentación de la API web para conocer cómo se utilizan las distintas funciones.
Las funciones de la API web se pueden llamar utilizando la clase Therefore.WebAPI.WebApiClient.
La creación de una nueva instancia requiere algunos parámetros. Como se describe en el ejemplo EmptyFunctionApp, el objeto “TheConfig” puede ayudar con ellos. Cuando configure un requestTimeout tenga en cuenta que la función propiamente dicha es llamada por una solicitud HTTP y agotará el tiempo de espera después de un máximo de 230 segundos. Credenciales: WebApiClient client = new WebApiClient(username, password, isToken, webApiUrl, tenant, language, requestTimeout, clientTimezoneIANA);
Token de portador JWT: WebApiClient client = new WebApiClient(bearerToken, webApiUrl, tenant, language, requestTimeout, clientTimezoneIANA);
Cuando se crea el objeto WebApiClient, pueden enviarse solicitudes utlizando la función “SendRequest”. Task<TResp> SendRequest<TReq, TResp>(TReq requestData)
Esta función individual puede emplearse para cubrir toda la funcionalidad de la API web. La solicitud está determinada por el tipo de parámetro de solicitud y respuesta. La solicitud y la respuesta deben coincidir. Por ejemplo, GetDocumentParams con GetDocumentResponse puede utilizarse para crear una solicitud “GetDocument”.
Ejemplo: Recuperar un documento (con archivos):
Pueden recuperarse documentos utilizando GetDocumentParams con GetDocumentResponse en la función SendRequest.
En el parámetro de solicitud GetDocumentParams debe especificarse el número de documento.
Si también deben recuperarse los archivos del documento, la propiedad “IsStreamsInfoAndDataNeeded” deberá establecerse como True. Si es suficiente con la información de flujo, sin los datos reales, “IsStreamsInfoNeeded” puede establecerse como True.
// Crear parámetros de solicitud GetDocumentParams parameters = new GetDocumentParams(); parameters.DocNo = docNo; parameters.IsStreamsInfoAndDataNeeded = true; parameters.IsIndexDataValuesNeeded = true;
// Enviar la solicitud GetDocumentResponse docResp = client.SendRequest<GetDocumentParams, GetDocumentResponse>(parameters).Result;
// Extraer todos los flujos de archivo al directorio especificado string extractDir = Path.GetTempPath(); foreach (var streamInfo in docResp.StreamsInfo) { string extractFileName = Path.Combine(extractDir, streamInfo.FileName); File.WriteAllBytes(extractFileName, streamInfo.StreamData); } |
---|
Respuesta: Todas las funciones que son llamadas por la nueva tarea de flujo de trabajo «Función de llamada» deben devolver algunos parámetros de devolución obligatorios en formato JSON en la respuesta HTTP: •TheError: Valor de cadena obligatorio. Si la cadena no está vacía, se mostrará en el historial del flujo de trabajo. La instancia se marcará con error, a menos que se haya solicitado un reintento en un momento posterior. •TheRetryAfterMin: Valor de entero opcional. Puede emplearse para solicitar un reintento desde Therefore™. Si se establece con un valor mayor que cero, la tarea ‘Función de llamada’ se comportará como una tarea ‘Espera’ y enviará una nueva solicitud cuando transcurra un número especificado de minutos (aproximadamente). Si se establece “TheError”, se tratará como una advertencia y la instancia de flujo de trabajo no se marcará con error. •TheRoutingDone: Valor booleano obligatorio, a menos que se especifique «TheRetryAfterMin». Puede establecerse como ‘true’ para indicar que ya ha realizado el enrutamiento de la instancia de flujo de trabajo utilizando la función de la API web «FinishCurrentWorkflowTask». Si se establece como ‘false’, Therefore™ enrutará la instancia de flujo de trabajo a la siguiente tarea. Si se especifica «TheRetryAfterMin», aún no se realizará el enrutamiento, por lo que en ese caso es opcional. Para simplificar la devolución de esos parámetros se creó la clase «TheRespParams». Mantiene todos esos parámetros como propiedades y proporciona la función «Serialize» para crear un objeto JSON. Hay dos formas de inicializar un objeto de esta clase: •Utilizando «TheError» y «TheRoutingDone» TheRespParams respParams = new TheRespParams(errMsg, routingDone); Este se debe utilizar como caso predeterminado o si se produce un error. Ambos valores son opcionales.
De forma predeterminada, errMsg está vacío y routingDone es ‘false’.
•Utilizando «TheError» y «TheRetryAfterMin» TheRespParams respParams = new TheRespParams(retryAfterMin, errMsg);
Esto indicareá a Therefore™ que reintente la llamada de función en otro momento. El valor entero de retryAfterMin debe ser mayor que cero; errMsg es opcional y se registrará como información adicional (no como error) en el historial del flujo de trabajo.
Solicitud:
Con cada llamada de función, Therefore™ envía los siguientes parámetros a la función como JSON con la solicitud HTTPS:
•TheWebAPIUrl: Mantiene el ajuste del servidor "URL de API web", que se puede configurar en la pestaña ‘XML Web Service’ o en la configuración avanzada. Este ajuste ya estará configurado correctamente cuando se utiliza Therefore™ Online, pero puede estar vacío si no se configura. •TheTenant: Nombre del inquilino actual. Estará vacío si el sistema no utiliza multiinquilino. •TheAccessToken: JWT-token que puede utilizarse para conectar con Therefore™ mediante la API Web. El token se configura para dar al usuario “$TheWFSystem” acceso al documento o expediente asociado con la instancia de WF actual, así como acceso al token y la instancia de WF actuales. Con los permisos concedidos es posible realizar cambios en el documento asociado, el expediente o los documentos asociados al expediente. Los permisos concedidos también deben permitir el enrutamiento de la instancias de flujo de trabajo a otra tarea. No se permite crear nuevos documentos o expedientes, ni ejecutar consultas. Tampoco se permite eliminar el expediente o el documento principal asociado con la instancias de flujo de trabajo o la propia instancias de flujo de trabajo. •TheInstanceNo: Número de instancia de la instancia de flujo de trabajo actual. •TheTokenNo: Número de token de la instancia de flujo de trabajo actual. •TheCaseNo: Número de expediente del expediente asociado con la instancia de flujo de trabajo. Este parámetro solo está disponible para flujos de trabajo de expediente. •TheDocNo: Número de documento del documento principal asociado con la instancia de flujo de trabajo. Este parámetro solo está disponible para flujos de trabajo de documento. •TheSettings: Objeto JSON (pasado como cadena) de todas las ‘Configuraciones de función personalizadas’ establecidas.
Puede estar vacío o utilizarse para pasar parámetros adicionales a la función llamada. El nombre y el valor de cada ajuste puede elegirse libremente, pero los nombres deben ser únicos de acuerdo con la especificación JSON. Todos los valores se tratan como valores de cadena y deben convertirse en otro tipo dentro de la función si es necesario.
Para simpliar la lectura de esos parámetros desde la cadena JSON se creó la clase "TheReqParams". Mantiene todos esos parámetros como propiedades y proporciona la función "Deserialize" para crear un objeto desde una cadena JSON.
El código de la clase “TheReqParams” puede emplearse como ejemplo para los ajustes personalizados.
Supongamos que ha establecido “TaskNo” con algún valor de entero en la configuración de tarea flujo de trabajo como “configuración de función personalizada”. Entonces podría utilizar la siguiente clase para leerlo.
class CustomSettings { public string taskNo; public int TaskNo { get { return int.Parse(taskNo); } set { taskNo = value.ToString(); } } public static CustomSettings Deserialize(string json) { return JsonConvert.DeserializeObject<CustomSettings>(json); } }
|
---|
¿Cómo se especifica un nombre de usuario y una contraseña para la conexión con la API web?
El código del proyecto de ejemplo comprobará fundamentalmente si se han especificado el nombre de usuario y la contraseña, y solo utilizará el token de acceso facilitado como último recurso. De esta forma, el valor predeterminado será el token a menos que especifique nombre de usuario y contraseña. Esas credenciales pueden ser necesarias si se requieren más permisos de los que proporciona el token.
TheConfig.cs utiliza el ConfigurationBuilder para acceder a “local.settings.json” y a las variables de entorno para buscar la configuración.
Cuando se ejecuta la función Azure localmente, el ajuste se leerá desde “local.settings.json”. Para añadir el ajuste, inserte los ajustes “WebAPIUser” y “WebAPIPassword” en el objeto JSON “Values” de este modo: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "WebAPIUser": "Domain\\TestUser1", "WebAPIPassword": "secretpassword" } }
Cuando se despliega la función en Azure, los ajustes se leerán desde la configuración de la aplicación.
Para añadir nombre de usuario y contraseña, seleccione su Function App en Azure Portal y haga clic en “Configuración de la aplicación”. Podrá añadir nuevos ajustes haciendo clic en “Nueva configuración de la aplicación”. Haga clic en "Guardar" cuando termine.
|
---|
Las funciones de Azure se pueden desplegar en la plataforma Microsoft Azure directamente en Visual Studio. Haga clic con el botón derecho en el proyecto y haga clic en “Publicar” para publicar la función de Azure. Seleccione “Azure Function App” para cargarla en Azure, directamente desde Visual Studio.
Si elige “Carpeta” deberá cargarla manualmente utilizando FTP. El nombre de Function App debe único globalmente, ya que se crea un pequeño sitio web “https://FunctionAppName.azurewebsites.net” que está disponible públicamente, con independencia de si la función se desencadena mediante HTTP o no (el desencadenador HTTP utilizará el mismo URL). Cada Azure Function App debe tener una cuenta de almacenamiento donde puedan almacenarse las funciones y los registros. Hay dos opciones distintas para hospedar una Function App:
•Plan de consumo
•Plan de App Service
Después de un despliegue correcto, la llamada a la función producirá un error 401 "No autorizado".
Se utiliza de forma predeterminada una clave de función para autorización que debe suministrarse con cada llamada.
Esta clave se puede obtener en el Azure Portal. Después de abrir la Function App, haga clic en “Administrar” y copie la clave “predeterminada”.
Una llamada desde Therefore™ incluirá clave como “x-functions-key” en el encabezado de la solicitud si está configurado.
La comprobación de la implementación sin Therefore™ se puede realizar utilizando cURL o un programa similar.
curl –d “” –i –X POST “https://AppName.azurwebsites.net/api/Route?code=FunctionKey”
Si no se define ninguna ruta se utilizará el nombre de función.
|
---|
© 2022 Therefore Corporation, todos los derechos reservados.