BLOG

Funciones remotas, driver ODBC y procesos web (III)

Por [N4] Paco Satué el | 4 Comments

En la última parte del artículo vamos a revisar las opciones que tenemos para extender nuestras aplicaciones de Velneo conectando con el mundo de Internet y las aplicaciones web.

En general hablaremos de aplicaciones web como aquellas herramientas que podemos usar para conectarnos a un servidor web, a través de Internet o de una intranet mediante un navegador.

La red Internet usa la familia de protocolos TCP-IP para interconectar e implementar aplicaciones de todo tipo y hoy en día no hay servicio que no tenga su versión para Internet.

El protocolo VATP, usado para conectar los componentes de Velneo entre sí, también pertenece a la familia de protocolos TCP/IP y por lo tanto también podemos desplegar nuestras aplicaciones Velneo en Internet.

De la misma manera que vClient conecta con vServer a través del protocolo VATP, las aplicaciones web intercambian información con el servidor web usando el HTTP (Protocolo de Transferencia de Hipertexto).

Por lo tanto, al igual que hemos hecho con las funciones remotas y el driver ODBC, necesitamos conectar nuestra Instancia de Velneo, que solo entiende comandos VATP, con una aplicación externa usando el protocolo HTTP.

Veamos entonces qué opciones tenemos los desarrolladores para establecer un puente entre los dos protocolos y conectar nuestras aplicaciones de manera sencilla al universo web.

Objeto Protocolo TCP-IP

De forma nativa, Velneo proporciona el objeto Protocolo TCP-IP con el fin de implementar un protocolo de comunicaciones TCP/IP propio en nuestras aplicaciones.

De este objeto nos interesa el subobjeto SERVER y el proceso asociado en el que, mediante los comandos de Protocolo TCP, programaremos el parseo de los mensajes HTTP que continen las partes REQUEST y RESPONSE intercambiadas con el cliente externo.

Para ejecutar el protocolo TCP en el servidor, añadimos al proyecto un proceso con el identificador ON_INIT_SERVER en el que escribimos un comando Protocolo TCP: iniciar servicio. Esto iniciará el servicio TCP en el vServer, escuchando en el puerto especificado en el objeto protocolo TCP del proyecto.

Si usamos un monitor de puertos veremos cómo el proceso vServer.exe está escuchando en 2 puertos. En la imagen siguiente vemos el puerto 690 para el protocolo VATP y el 54240 para el protocolo HTTP que usará el objeto protocolo TCP.

El protocolo TCP estará en funcionamiento en el servidor vServer hasta que se ejecute un comando Protocolo TCP: terminar servicio desde un proceso en tercer plano.

El objeto Protocolo TCP se puede iniciar desde un proceso tanto en primer plano como en el tercer plano.

Nuestro primer proceso web

Veamos un ejemplo básico de cómo programar el intercomunicador SERVER del protocolo TCP para que funcione como un proceso web, encargado de convertir un mensaje HTTP en los comandos VATP que entiende el servidor vServer de Velneo.

El código lee y parsea el Request del mensaje HTTP usando el comando Protocolo TCP: Recibir línea.

Del parseo del Request se obtienen las 4 partes siguientes del mensaje HTTP:

  • Línea de inicio – Primera línea con la petición a ejecutar GET, POST o PUT.
  • Cabeceras HTTP – Información sobre el mensaje.
  • Línea vacía – Señala el comienzo del cuerpo con los datos del mensaje.
  • Cuerpo – Datos de la petición.

Una vez analizado el Request, el proceso web enviará los comandos de ejecución a la Instancia de datos usando el protocolo VATP. Con el resultado devuelto por la Instancia de datos el proceso web construye el Response y devuelve el mensaje al cliente HTTP.

La petición GET para este ejemplo tendrá el siguiente aspecto:

http://dominio:puerto/?funcion=[FUNCION]&datos=[DATOS]

El parámetro funcion determina la función remota que se va a ejecutar y el parámetro datos indica el formato de los datos devueltos, json o csv.

En la primera parte del artículo utilizamos el componente vClient sin GUI para ejecutar funciones remotas desde una aplicación externa. Con este proceso web conseguimos lo mismo, habilitar la ejecución de funciones remotas desde un cliente HTTP.

// Rem ( Proceso web - Intercomunicador SERVER del objeto Protocolo TCP )
// Rem ( El proceso se ejecuta cuando se recibe una petición TCP-IP al puerto de escucha )
Rem ( La petición tendrá un formato de Mensaje HTTP )
Set ( LDATOS_RESPONSE, 1 )
Set ( CRESUL, “0” )
// Rem ( LÍNEA DE INICIO con la Petición del Mensaje HTTP )
Protocolo TCP: Recibir línea ( HTTP_LINEA_INI, 1 )
// Rem ( PARÁMETROS. Leemos los parámetros del Mensaje HTTP )
Set ( HTTP_PARA_FUN, getStringRegExp(HTTP_LINEA_INI, "funcion=([a-zA-Z\\d\\_]*)", 0, 0, 1) )
Set ( HTTP_PARA_DATOS, getStringRegExp(HTTP_LINEA_INI, "datos=([a-zA-Z,\\d\\_]*)", 0, 0, 1) )
// Rem ( CABECERAS HTTP )
Set ( LSEGUIR, 1 )
For ( NLIN, 0, LSEGUIR, 1 )
   Protocolo TCP: Recibir línea ( CLINEA, 1 )
   Set ( HTTP_CABECERAS, HTTP_CABECERAS + CLINEA )
   If ( CLINEA = "\r\n" )
      // Rem ( LÍNEA VACÍA )
      Set ( LSEGUIR, 0 )
Set ( HTTP_REQUEST, HTTP_LINEA_INI + HTTP_CABECERAS )
// Rem ( CUERPO DEL MENSAJE. Existe si el verbo es POST o PUT )
If ( indexOfString("POST,PUT ", left(HTTP_LINEA_INI, 4), 0, 0) > -1 )
   Rem ( Tamaño del Cuerpo del mensaje )
   Set ( HTTP_CUERPO_LONG, stringToNumber(getStringRegExp(HTTP_REQUEST, "Content-Length: (\\d*)", 0, 0, 1)) )
   Protocolo TCP: Recibir buffer ( HTTP_DATOS, HTTP_CUERPO_LONG, 3 )
   Set ( HTTP_REQUEST, HTTP_REQUEST + HTTP_DATOS )
Libre
// Rem ( RESPONSE. Respuesta que enviamos al cliente HTTP )
// Rem ( LÍNEA DE INICIO de la Respuesta al Mensaje HTTP )
Set ( HTTP_RESPONSE, "HTTP/1.0 200 OK\r\n" )
Rem ( Devolver un Estado 400 en caso de error - Request erróneo )
// Rem ( EJECUTAR PROCESO. Depende del parámetro )
Set ( LRESUL_JSON, HTTP_PARA_DATOS = "json" )
If ( HTTP_PARA_FUN = "ART" )
   Rem ( Lista de Artículos desde vGestion - http://<dominio>:puerto/?funcion=ART&datos=json|csv )
   Set ( CRESUL, rfc: FUN_REM_ART(_CSERVIDOR, _CINSTANCIA, _CUSUARIO, _CPASSWORD, LRESUL_JSON) )
Else if ( HTTP_PARA_FUN = "CLT" )
   Rem ( Lista de Clientes desde vGestion - http://<dominio>:puerto/?funcion=CLT&datos=json|csv )
   Set ( CRESUL, rfc: FUN_REM_CLT(_CSERVIDOR, _CINSTANCIA, _CUSUARIO, _CPASSWORD,LRESUL_JSON) )
Else if ( HTTP_PARA_FUN = "VTA_FAC_CLT" )
   Rem ( Facturas de Venta de un Cliente - http://<dominio>:puerto/?funcion=VTA_FAC_CLT> )
   Rem ( El ID del Cliente se obtiene del cuerpo del mensaje - parámetro id_clt en el POST )
   Set ( HTTP_PARA_DATOS, getStringRegExp(HTTP_DATOS, "id_clt=([a-zA-Z,\\d\\_]*)", 0, 0, 1) )
   Set ( CRESUL, rfc: FUN_REM_VTA_FAC_CLT(_CSERVIDOR, _CINSTANCIA, _CUSUARIO, _CPASSWORD, HTTP_PARA_DATOS) )
Else if ( HTTP_PARA_FUN = "CSS" )
   Rem ( Descarga la Hoja de Estilo. El parámetro contiene el ID- http://<dominio>:puerto/?funcion=CSS)
   Set ( CRESUL, rfc: HOJA_ESTILO(_CSERVIDOR, _CINSTANCIA, _CUSUARIO, _CPASSWORD, HTTP_PARA_DATOS) )
Else if ( HTTP_PARA_FUN = "IMG_CSS" )
   Rem ( Descarga la Lista de Imágenes CSS - http://<dominio>:puerto/?funcion=IMG_CSS&datos=json )
   Set ( CRESUL, rfc: FUN_CSS_IMG_EXPORTAR(_CSERVIDOR, _CINSTANCIA, _CUSUARIO, _CPASSWORD, HTTP_PARA_DATOS) )
Libre
Rem ( El 0 se considera ERROR )
If ( CRESUL = "0" )
   Set ( LRESUL_JSON, 0 )
   Set ( CRESUL, "##ERROR## ejecutando la Función remota FUN_REM_" + HTTP_PARA_FUN )
   Set ( HTTP_RESPONSE, "HTTP/1.0 400 BAD REQUEST\r\n" )
Libre
If ( LDATOS_RESPONSE = 1 )
   // Rem ( CABECERAS. Tamaño de la Respuesta y tipo de contenido de los DATOS )
   Rem ( Datos codificados en UTF-8 (Header charset=UTF-8) )
   Set ( CRESUL_UTF8, stringToAscii(CRESUL, "UTF-8") )
   Set ( NRESUL_LEN, len(CRESUL_UTF8) )
   Set ( HTTP_RESPONSE, HTTP_RESPONSE + "Content-Length: " + NRESUL_LEN + "\r\n" )
   Set ( HTTP_RESPONSE, HTTP_RESPONSE + "Content-Type: " + choose(LRESUL_JSON, "application/json", "text/plain") + "; charset=UTF-8\r\n" )
   // Rem ( LÍNEA VACÍA )
   Set ( HTTP_RESPONSE, HTTP_RESPONSE + "\r\n" )
   // Rem ( DATOS con el Resultado )
   Set ( HTTP_RESPONSE, HTTP_RESPONSE + CRESUL_UTF8 )
Libre
// Rem Envía el Response al cliente HTTP
Protocolo TCP: Enviar buffer ( HTTP_RESPONSE, len(HTTP_RESPONSE), 10 )
Libre

La imagen siguiente muestra el resultado de un mensaje HTTP con el comando GET. Se solicita la ejecución de la función remota ART con el formato de salida JSON. Como cliente HTML se ha usado el Visor HTML nativo de Velneo.

Para montar un servidor HTTP en Velneo Cloud hay que solicitar a Velneo el servicio TCP en Cloud. Tendrás disponible un conjunto de puertos para usar en las peticiones HTTP.
Por ejemplo http://c3.velneo.com:10193/?funcion=ART&datos=csv.

Con este sencillo código tenemos nuestro primer proceso web preparado para intercambiar información con cualquier aplicación externa a través del protocolo HTTP. Nuestra herramienta de análisis de datos Excel ya puede descargar información de la Instancia de datos con una simple consulta Web.

Este sería el funcionamiento básico de un servidor HTTP, pero por supuesto necesitamos muchas más funcionalidades si pretendemos poner en producción nuestra aplicación conectada a un entorno web público.

Afortunadamente Cristian Vásquez ha desarrollado CIRRUS y lo ha puesto al servicio de la comunidad de desarrolladores de Velneo. Es una Open App con una calidad técnica de primer orden, que podemos usar con total garantía.

CIRRUS

Cirrus se autodefine como una implementación en JavaScript del protocolo HTTP corriendo directamente sobre el objecto TCP/IP de Velneo V7, que permite servir código HTML, JSON, Javascript y CSS directamente desde una app Velneo v7.

El esquema de funcionamiento de Cirrus se basa en un modelo de tres componentes denominados Rutas, Controladores y Renderizado.

Cirrus.js y el objeto global wAPP

Todo el código de CIRRUS reside en el archivo javascript cirrus.js, en el que se define el objeto global wAPP y toda la lógica de parseo y renderizado a partir del mensaje HTTP.

En el archivo api_server.js se establecen las rutas y controladores y finalmente se procesa el Request renderizando el Response correspondiente.

Rutas

Las rutas son el intermediario entre las peticiones HTTP y la lógica de negocio que se encuentra en los controladores de la aplicacción.

Las rutas se añaden a la propiedad router de wAPP mediante la función addRouters().

Cada método de petición HTTP se dirige a un controlador con la acción correspondiente, separados por un caracter de almohadilla #. También podemos mapear directamemte objetos nativos de Velneo de tipo Procesos y Búsquedas.

En el código siguiente añadimos las rutas para las tablas de Clientes y Artículos. Las rutas envían la petición del mensaje HTTP a los controladores cltController y artController respectivamente.

wApp.router.addRoutes({
    // Clientes
    "GET /clt": "cltController#index",
    // Artículos. La Ruta resource crea las 7 rutas REST
    /*
    Los parámetros se marcan con los dos puntos o de la forma habitual param=valor
    En el body los parámetros se introducen en formato URL encoded 
    GET /art            "artController#index"	Lista de artículos
    GET /art/:id        "artController#show"	Mostrar un artículo
    PUT /art/:id        "artController#update"	Actualizar un articulo desde el Body
    POST /art           "artController#create"	Crea un articulo desde el Body
    DELETE /art/:id     "artController#delete"	Eliminar un artículo
    GET /art/new        "artController#new"     Nuevo artículo     (con formulario HTML)
    GET /art/:id/edit   "artController#edit"    Editar un artículo (con formulario HTML)
    */
    "resource art": "art",
    // Procesos y Querys. Extensión .pro y .bus
    // El resultado se devuelve en la variable local RESULT
    // Si no existe RESULT se devuelve vRegisterListToJSON de la salida del proceso
    // Se puede pasar la lista de campos en el parámetro ?fields=id,name,...
    "GET /art.pro": cAlias_dat + "/PRO_HTTP_ART",	
    "GET /art.bus": cAlias_dat + "/BUS_HTTP_ART",
});

Los controladores son los mediadores entre los Mensajes HTTP y el acceso a los datos. El controlador es el código que accede a la Instancia de datos para devolver una respuesta usando un objeto JSON.

Controladores

El controlador se añade a wAPP como un objeto javascript con el nombre del controlador seguido de la palabra Controller. Las acciones del controlador se definen como funciones del controlador.

A continuación tienes el código del controlador artController, con las acciones de Listar, Mostrar, Editar, Añadir y Eliminar artículos.

wApp.artController = {
    // LISTA de artículos
    // Usar GET -> http://dominio:puerto/art[.json]
    "index": function(params) {
        return ({articulos: getArt()});
    },
    // MOSTRAR un Artículo - GET -> http://dominio:puerto/art/:ID[.json]
    // params es un json con el :ID de la URL
    "show": function(params){
        var registros = new VRegisterList(theRoot);
        registros.setTable("vgestion_dat/ART");
        registros.load("ID", [params.id]);
        var aCampos = "ID,NAME,FAM,REF,COD_BAR,PRE_VTA".split(",")
        return (wApp.vRegisterListToJSON(registros, aCampos));
    },
    // EDITAR un Artículo - PUT -> http://dominio:puerto/art/:ID
    // params es un json con el :ID de la URL y con los 
    //  datos del Body como urlencoded - name=NOM_ART&ref=REF_ART
    "update": function(params) {
        if ( theRoot.beginTrans("Transacción en Mensaje HTTP - Modificar artículo")) {
            var registros = new VRegisterList(theRoot);
    	    registros.setTable("vgestion_dat/ART");
            registros.load("ID", [params.id])
            var articulo = registros.readAt(0)
    	    articulo.setField("NAME", params.body.name)
            articulo.setField("REF", params.body.ref)
    	    articulo.modifyRegister()
    	    theRoot.commitTrans();
        }
        return ({message: "Artículo modificado"})
    },
    // CREAR un nuevo Artículo - POST -> http://dominio:puerto/art
    // params es un json con los datos del Body como urlencoded - campo1=valor2&campo2=valor2
    // con Header --> Content-Type: application/x-www-form-urlencoded
    "create": function(params) {
    	if ( theRoot.beginTrans("Transacción en Mensaje HTTP - Nuevo artículo")) {
            var articulo = new VRegister(theRoot);
            articulo.setTable("vgestion_dat/ART");
            articulo.setField( "NAME", params.body.name);
            articulo.setField( "REF", params.body.ref);
            articulo.setField( "FAM", params.body.fam);
            articulo.setField( "COD_BAR", params.body.cod_bar);
            // Se guarda el registro				
            articulo.addRegister();
            theRoot.commitTrans();				
    	}			
    	return ({message: "Nuevo artículo: " + articulo.fieldToInt("ID")});
    },
    // ELIMINAR un Artículo - DELETE -> http://dominio:puerto/art/:ID
    // params es un json con el :ID de la URL
    "delete": function(params) {
    	if ( theRoot.beginTrans("Transacción en Mensaje HTTP - Eliminar artículo")) {
            var registros = new VRegisterList(theRoot);
            registros.setTable("vgestion_dat/ART");
            registros.load("ID", [params.id])
            var articulo = registros.readAt(0)
            articulo.deleteRegister()
            theRoot.commitTrans();
    	}
    	return ({message: "Artículo eliminado"})
    }
}

Los controladores pueden ser ampliados con características opcionales como filtros, autenticación básica y cabeceras personalizadas.

Renderizador

El módulo Renderizador recoge la salida del Controlador y entrega un Response con los datos en el formato solicitado por la parte Request del mensaje HTTP. Por defecto la salida es json y además podemos obtener html, xml o css.

Veamos algunos ejemplos que devuelven exclusivamente json:

Para facilitar y ampliar el renderizado de código html, Cirrus implementa opcionalmente un sistema por el cual una Acción del Controlador se asocia a una Vista, la cual define una plantilla o template en la que podemos incrustar la salida del controlador, preprocesando el json mediante la herramienta Handlebars. Puedes ampliar información en la documentación de cirrus.

Servidor Web en Velneo V7

En el servidor HTTP que hemos visto hasta ahora, el proceso web (intercomunicador SERVER) es cargado y ejecutado en cada petición del cliente HTML, debido a la forma en que Velneo implementa el objeto Protocolo TCP. Por lo tanto, puede que no reuna los requisitos de eficiencia, seguridad y concurrencia que se necesita en un entorno público de internet.

Velneo, desde sus inicios, no se ha planteado la opción de incorporar un servidor HTTP dentro de vServer, en su lugar se optó por aprovechar la tecnología de un servidor Web externo.

Separar el servidor HTTP del servidor VATP permite asegurar nuestros datos ante cualquier ataque, como el de denegación de servicio, ya que no afectará al vServer y será el servidor Apache el que recibe todo ese tráfico.

Para conseguir el objetivo de facilitar la conexión de nuestras aplicaciones web externas con la instancia de datos de vServer a través del protocolo HTTP, Velneo tuvo que resolver tres aspectos:

  • Primero, seleccionar un servidor Web multiplataforma con características avanzadas. La elección fue Apache, un proyecto open source de servidor HTTP.
  • Segundo, diseñar un módulo denominado vModApache que conecte el servidor HTTP Apache con el servidor VATP vServer de Velneo.
  • Y por último establecer un mecanismo que permita habilitar algunos objetos del proyecto como accesibles desde la web.

Servidor web Apache

El servidor Apache es una opción fiable y muy utilizada entre los servidores web disponibles en Internet. Es una plataforma estable y robusta con un desarrollo continuo por parte de la Apache Software Foundation.

Para nuestras pruebas en local disponemos de varios paquetes compilados de Apache, que además instalan PHP y MySQL. Uno de los paquetes más habituales es Bitnami WAMP Stack.

Velneo requiere una versión 2.4.X de Apache con soporte de OpenSSL 1.1.

Módulo vModApache

El módulo vModApache se ha diseñado como puente entre los mensajes HTTP de Apache y el servidor vServer. Nos permitirá enviar comandos VATP a nuestra instancia de aplicación de vServer usando el protocolo HTTP de la Web.

Es un componente equivalente a vClient, pero sin GUI, y por lo tanto dispone de todas las ventajas de este componente, como la caché de datos en memoria y el refresco terciario, así como la caché local de archivos a nivel de usuario.

El servidor Apache usará el módulo vModApache en multitarea y con múltiples vServer. La configuración determina cuantos enganches puede establecer vModApache y los límites, dependiendo del número de conexiones simultáneas que queramos soportar.

La instalación de vModApache copia el archivo mod_velneo.so en el directorio de Velneo por defecto %programfiles%\Velneo, de esta forma tiene acceso a las librerías Qt necesarias.

La instalación de vModApache se puede realizar en los sistemas operativos de Windows y Linux. En Windows la versión de 32 o 64 bits coincidirá con la del servidor Apache.

Carga del módulo vModApache

Para que el servidor Apache pueda usar el módulo vModApache debemos especificar la ruta en el archivo de configuración de directivas conf/httpd.conf.

#Carga el módulo de Velneo
LoadModule velneo_module "C:/Archivos de programa/Velneo/mod_velneo.so"

Conexión de vModApache con vServer

La conexión del módulo vModApache al servidor vServer se configura en la directiva Location o también en la directiva Virtualhost. Al igual que vClient, el módulo vModapache solo se puede conectar a instancias de aplicación y accederá al resto de proyectos de la solución a través de la herencia.


# La directiva Location define un subdirectorio o ruta_base en nuestro dominio web
# https://nombre_servidor/ruta_base/recurso.pro
<Location /ruta_base>
   setHandler velneo
   [VelneoMode SERVER]
   Vrl vatps://usuario:contraseña@servidor\[:NUM_PUERTO]/ID_INSTANCIA_APP
</Location>
----
# La directiva VirtualHost redirige un puerto a un dominio
# https://nombre_servidor/recurso.pro
<VirtualHost *:número_puerto>
   ServerName nombre_servidor
   setHandler velneo
   [VelneoMode SERVER]
   Vrl vatps://usuario:contraseña@servidor\[:NUM_PUERTO]/ID_INSTANCIA_APP
</VirtualHost>

Cuando se inicia el servidor Apache la directiva Location o VirtualHost ejecuta la conexión con el vServer usando la VRL del protocolo VATP. Se creará un enganche a la instancia de la aplicación ID_INSTANCIA_APP con el usuario y contraseña indicados. A partir de entonces todas las peticiones HTTP a través de la senda ruta_base o número_puerto de la URL se encolan por vModApache al enganche establecido en vServer contra la instancia de aplicación.

Procesos en local o en el servidor

Cuando aparece la opción VelneoMode SERVER, los procesos web se ejecutarán en el tercer plano. De la misma manera que hacemos en vClient con el comando Ejecutar proceso, vModApache dispone de la opción de 1P o 3P. Los procesos en 1P pueden usar el GUI, como por ejemplo el comando Exportar informe a fichero (solo en windows).

Variables globales y tablas en memoria usando vModApache

Las tablas en memoria que se usan en local desde vModApache, serán compartidas por todas las llamadas sucesivas que usen el mismo enganche. Lo mismo ocurrirá con las variables globales en memoria.

Caché local de archivos

El módulo vModApache crea una caché de archivos local en la carpeta cacherun dentro del perfil de usuario. El usuario con el que se ejecuta vModApache es el usuario asignado al servicio Apache.

En windows, por defecto el usuario es system y la cacherun se crea en la carpeta:

C:\Windows\System32\config\systemprofile\Velneo\cacherun\<ip_vServer>

En Cloud la caché local se crea en

//Velneo/cacherun/localhost_<puerto>/

Más detalles de la configuración de vModApache en la documentación de Velneo

Velneo Cloud con vModApache

Velneo proporciona en su Cloud el servicio Velneo Cloud vModApache que incluye la combinación servidor Apache y módulo vModApache junto al servidor vServer.

La URL de acceso al servidor Apache se utiliza usando el protocolo seguro https. Velneo proporciona los certificados autofirmados para las pruebas de nuestra aplicación.

Mediante SFTP podemos acceder a las carpetas de Apache y al fichero de configuración 001-default-ssl.conf

# Configuración de Velneo Cloud vModApache
# Logs del servidor Apache
CustomLog /home/${USERNAME}/apache2/logs/access.log combined
ErrorLog /home/${USERNAME}/apache2/logs/error.log
 # Servidor Apache en el puerto seguro 443
 <VirtualHost *:443>
	DocumentRoot /home/${USERNAME}/apache2/html
	<Directory /home/${USERNAME}/apache2/html/>
        	Options FollowSymLinks
	        AllowOverride None
        	Require all granted
	</Directory>
	# Certificados autofirmados
	SSLCertificateFile /etc/apache2/external/cert.crt
	SSLCertificateKeyFile /etc/apache2/external/cert.key
	ServerName c3.velneo.com
	<Location /miapp>
	  setHandler velneo
	  [VelneoMode SERVER]
	  # VRL de acceso a la instancia de aplicación en el cloud
 	  Vrl vatps://<usuario>:<contraseña>@localhost:10190/INSTANCIA_APP
	</Location>
</VirtualHost>

La recomendación de Velneo es instalar una combinación de Apache/vModApache única por cada instancia que vayamos a usar con la aplicación externa web.

Hay varias incidencias con VModApache en Cloud a tener en cuenta:
Comando Informe: Exportar a fichero PDF no funciona en Linux
La función setRawBody() de theResponse no funciona en Modo normal
Carpeta cacherun (sysCacheClientPath) no funciona en Modo normal

Objetos accesibles web

Un cliente HTTP externo puede tener acceso a través de vModApache a los siguientes objetos de nuestros proyectos de Velneo:

Procesos

Los procesos nativos y javascript deben tener el estilo Accesible web activado (a partir de ahora los llamamos también procesos web). Los procesos web nativos devuelven información con el comando Set dato de retorno y los procesos web javascript disponen para este cometido del objeto API theResponse.

El comando Set dato de retorno solo es operativo en los objetos función, tal como confirma la documentación, y es curioso que solo sea funcional en los procesos web.

Los procesos web nativos pueden recibir parámetros que se asignarán automáticamente a las variables locales del mismo nombre. Los procesos web de javascript usarán el objeto del API theRequest para obtener las claves del GET o del POST.

La codificación del texto devuelto desde los procesos nativos (ISO-8859-1, UTF-8, …) se determina mediante la variable local CONTENT_TYPE en la que introducimos el valor de la cabecera Content-Type del Response. Los procesos javascript harán uso del objeto theResponse y la función setContentType().

Por ejemplo, los procesos que devuelven JSON tienen que incluir una línea como la siguiente:

proceso nativo - Set ( CONTENT_TYPE, "application/json; charset=UTF-8")
proceso javascript - theResponse.setContentType("application/json; charset=UTF-8")

Existe una incidencia con la variable local CONTENT_TYPE cuando el proceso se ejecuta en tercer plano (directica VelneoMode SERVER). En ese contexto el proceso nativo no devuelve la cabecera con la codificación del texto.

Si el proceso devuelve código HTML usaremos un elemento meta con atributo charset en la etiqueta <head>.

<head>
   <meta charset='UTF-8'>
   <title>...</title>
</head>

Dibujos estáticos

Los Dibujos estáticos son de solo lectura declarados en los proyectos. Se pueden obtener en formato jpg y png. El formato png conserva el canal alfa con la transparencia.

Campos de tipo objeto dibujo

Son campos con contenido gráfico en las tablas del proyecto de datos. Están almacenados en el archivo contenedor cnd de la base de datos, con compresión JPEG al 100% de calidad.

Otras imágenes y archivos externos

Los ficheros adjuntos del proyecto se copian a la carpeta de caché de archivos (cacherun), localizada en el perfil del usuario que ejecuta el cliente vModApache. Los procesos ejecutados en local (sin la directiva VelneoMode SERVER) disponen de la variable del sistema sysCacheClientPath con la que tendrán acceso a los archivos adjuntos en la carpeta cacherun.

Los procesos que devuelven código HTML pueden usar enlaces relativos a la carpeta DocumentRoot del servidor Apache. Cada directiva Virtualhost puede tener su propio DocumentRoot.

URL de acceso a los objetos accesibles web

Desde el cliente HTTP usaremos las siguientes URL’s para acceder a los recursos de la aplicación Velneo.

La URL base de todos los recursos desde Velneo es http[s]:dominio/Location/.

Objeto URL
URL base http[s]:dominio/Location/ (busca index.[pro] si existe)
Proceso [Id_Proyecto_ext]/[proceso][.pro]?par1=val1&par2=val2..
Dibujo [Id_Proyecto_ext]/Id_dibujo.[jpg][png]
Campo Dibujo obj/[Id_Proyecto_ext]/Id_tabla/#campo.jpg/png
DocumentRoot ../archivo (path relativo a Location)

Id_Proyecto es el Nombre del proyecto (no el Alias) al que pertenece el objeto y hay que respetar las mayúsculas/minúsculas. Se añade la _ext app o dat en minúsculas para identificar el proyecto de aplicación a de datos. No es necesario indicar esta parte de la URL cuando el proceso es del proyecto principal indicado en la VRL de conexión.

El nombre del proceso es el endpoint final que llevará opcionalmente la extensión .pro. Si no se especifica se buscará en el proyecto un proceso con identificador INDEX.

Los nombres de los parámetros deben coincidir con Variables locales del proceso.

Los dibujos estáticos se descargan directamente con el identificador, seguido de la extensión que determina el formato jpg o png.

Id_dibujo es el identificador del objeto dibujo en el proyecto principal o heredado.

El contenido de los campos de tipo dibujo se obtiene usando el prefijo especial obj/. El formato puede ser jpg o png. El prefijo obj/ indica que se trata de un campo objeto y es obligatorio.

Id_tabla es el identificador de la tabla en el proyecto de datos.

El valor del campo de tipo dibujo #campo corresponde al contenido en el archivo .dat y es un puntero o etiqueta que apunta a la imagen jpeg archivada en el contenedor .cnd de la base de datos.

El prefijo obj/ solo funciona para proyectos de datos que heredan directamente del proyecto principal. En el esquema de la imagen siguiente, para acceder a un campo de la tabla en vgestion_dat, habrá que crear una rama de herencia entre el proyecto principal y el proyecto de datos vgestion_dat. VDevelop nos marcará esta herencia como redundante.

Servir contenido HTML

Cuando la aplicación Velneo sirve código HTML al cliente web, éste renderiza el HTML teniendo en cuenta que los enlaces toman como carpeta base la URL base http[s]:dominio/Location/.

Si queremos servir un archivo de la carpeta principal de Apache usaremos la ruta relativa.

<img src='../mi_logo.png'></img>

En el siguiente ejemplo se usan los cuatro tipos de enlaces relativos al URL base.

https://c3.velneo.com:10192/miapp/index

La Lista de artículos accede a los dibujos de la tabla a través del prefijo obj/ y de la herencia redundante.

https://c3.velneo.com:10192/miapp/pro_http_art_img

API REST

El siguiente paso, para aprovechar todo el potencial de los procesos web y vModApache fue crear en la versión Velneo 7.19 un API REST para estandarizar el acceso de aplicaciones externas a los datos de nuestra aplicación usando el protocolo HTTP.

Un API (Interfaz de Programación de Aplicaciones) es el conjunto de funciones y procedimientos que ofrece una bibliotea o librería para ser utilizada por una aplicación externa como una capa de abstracción.

REST (Transferencia de Estado Representacional) es cualquier interfaz entre sistemas que use HTTP para obtener y manipular datos en todos los formatos posibles, como XML y JSON. Es una alternativa más sencilla a otros protocolos estándar de intercambio de datos como SOAP (Simple Object Access Protocol), mucho más complejos. RESTful es un servicio web que implementa la arquitectura REST.

Las características principales del API Rest de Velneo son las siguientes:

  • Es Estándar porque está basado en la sintaxis jsonapi.org con respuestas json. La cabecera ContentType de las respuestas siempre es «application/json; charset=utf-8«.
  • Seguridad implementada en cuatro niveles: protocolo https a través de Apache, uso de API Key, objetos privados y sistema de permisos por objetos.
  • A los recursos indicados en la URL se aplican operaciones de listado, modificación y borrado a través de los métodos GET, POST y DELETE respectivamente. En otras aplicaciones se usa el método PATCH para actualizar y PUT para reemplazar.
  • Autodocumentado con Swagger. Usando Swagger UI para exponer la documentación de nuestra API podemos organizar nuestros métodos y probar los diferentes métodos HTTP.

El módulo API Rest de Velneo viene incluido en la plantilla de código abierto vERP. La documentación del API Rest la encontrarás en la parte correspondiente a vERP.

Proceso web V1

El módulo principal del API Rest de Velneo se ha programado en un solo proceso web llamado V1. El proceso V1 usa los procesos javascript v1.js y api_rest_funciones_v1.js.

Tiene las siguientes funcionalidades:

  • Gestiona el recurso al que queremos acceder: tabla, proceso (_process) o búsqueda (_query).
  • Acepta 3 métodos de operación: GET, POST y DELETE.
  • Construye el JSON que siempre devolverá en el cuerpo de la respuesta theResponse.
  • Requiere un API key (tabla API_KEY_W) para determinar el nivel de acceso.
  • No es necesario que los procesos tengan estilo Acceso Web ya que es el API key el que determina el acceso.
  • Siempre devuelve el código de estado 200. La descripción del error se devuelve en el cuerpo de la respuesta theResponse.

El proceso web V1 recibe el objeto theRequest de la petición HTTP desde vModApache, lo parsea y obtiene las diferentes partes de la URL. Se ejecuta el método HTTP sobre el recurso solicitado, obteniendo una lista en formato JSON, que se envía al objeto de salida theResponse.

La URL con la petición al API Rest se compone de las siguientes partes:

  • Método HTTP
  • Nombre del proyecto de aplicación o de datos que contiene el proceso web V1
  • Nombre del proceso web (por defecto V1)
  • Tipo de Recurso y nombre del Recurso
  • Parámetros de la petición HTTP
  • Valor del API key

En el proceso V1 existe un objeto global uriObjeto con todos los componentes de la URL y la configuración de acceso y seguridad.

Propiedad Valor
api_key API key de acceso al recurso
metodo GET | POST | DELETE
recurso <nombre tabla> | _process | _requery
tabla Identificador alias_proyecto/id_tabla
proceso Identificador alias_proyecto/id_proceso
busqueda Identificador alias_proyecto/id_busqueda
identificador ID de tabla | id_proceso | id_busqueda
fields_tablas Array – Parámetro fields
fields_campos Array – Parámetro fields=campo1,camp2, …
sort Array – Parámetro sort=campo1,camp2, …
filter Array – Parámetros filter[índice]=valor
page Parámetro page[number] y page[size]
param Parámetro param[var local]=valor
body Método POST – Contenido JSON del Body
errores Array – Mensajes de error
seg_metodos Array – Métodos HTTP soportados
seg_campos Array – Campos devueltos de la tabla
seg_procesos Array – Procesos con o sin salida de la tabla
seg_busquedas Array – Búsquedas de la tabla

El recurso solicitado en la URL se busca en la herencia de todos los proyectos de la Solución, por lo tanto es conveniente que el ID del recurso sea único en toda la herencia.

Recursos con salida tabla

El API key determina los métodos HTTP que podemos usar, así como los campos, procesos y búsquedas asociados a la tabla que serán accesibles a través de la URL.

Para el método POST los datos del Body se reciben en la variable local BODY del proceso. Los campos de tipo Objeto Dibujo reciben el contenido codificado en Base64.

Veamos algunos ejemplos:

Hay un problema con la seguridad de acceso que debemos tener en cuenta.
El parámetro fields permite el acceso a campos Privados a través de los punteros a Maestro.

Recursos (procesos) sin salida

El API key determina los métodos HTTP que podemos usar con los procesos sin salida.

Los procesos reciben parámetros en la variables locales y devuelven un resultado JSON personalizado en la variable local RETORNO. Para el método POST los datos del Body se reciben en la variable local BODY.

El proceso web en Modo Local no funciona cuando en el Path no aparece el nombre del proyecto de Aplicación.
https://c3.velneo.com:10192/miapp/v1/art?api_key=apiVelneo No funciona
https://c3.velneo.com:10192/miapp/0PS_ARTIPACO_app_app/v1/art?api_key=apiVelneo Funciona

Proceso web SWAGGER

Para cumplir con el requisito de ser autodocumentado, el API Rest de Velneo ha desarrollado un proceso web con el nombre SWAGGER, con la misión de generar dinámicamente un fichero de definición del API disponible en la aplicación.

Swagger es una serie de reglas, especificaciones y herramientas que nos ayudan a documentar nuestras APIs.

En nuestra aplicación, la documentación del API Rest es accesible con la url siguiente:
https://c3.velneo.com:10192/miapp_server/swagger?api_key=apiVelneo

El JSON devuelto por el proceso swagger puede ser analizado de forma gráfica mediante la aplicación swagger UI.

Instalación de swagger UI en local

Usar la versión 2 de Swagger que es la que usa Velneo.
Descargar Swagger UI desde https://github.com/swagger-api/swagger-ui/releases/tag/v2.2.10.
Copiar la carpeta _dist del zip a una subcarpeta de Apache, por ejemplo /apache2/htdocs/_swagger.
Editar index.html y cambiar el url inicial.

Acceso al Swagger UI en https://c3.velneo.com:10192/_swagger/

La herramienta Swagger UI nos presenta los Recursos disponibles en el API y los parámetros necesarios, así como un ejemplo del URL de acceso a través del proceso web V1.

Conclusiones

En esta última parte del artículo hemos explorado las herramientas que Velneo nos proporciona para conectar con aplicaciones externas, usando procesos web y el protocolo HTTP.

De forma nativa disponemos de un servidor HTTP básico con el objeto Protocolo TCP-IP y el subobjeto SERVER. Con Cirrus potenciamos el servidor TCP para facilitar la creación de nuestro propio servicio web, incluso renderizar HTML y llegar a construir un verdadero servidor web. Velneo puede habilitarnos un servicio TCP en Cloud para dar conectividad HTTP a nuestras aplicaciones Velneo a través de Internet.

Para disponer de mayor eficiencia, seguridad y concurrencia en un entorno público de Internet, Velneo ha desarrollado el módulo vModApache para conectar nuestro vServer con el servidor HTTP Apache. Se establece entonces un mecanismo de acceso a los objetos de nuestra Solución desde la web y una estructura de las URL para las peticiones HTTP. Velneo proporciona en el Cloud su servicio Velneo Cloud vModApache.

Finalmente, Velneo pone a nuestra disposición un API Rest que cumple con la especificación jsonapi.org, para que sea un estandar el acceso a nuestros datos desde cualquier aplicación web.

Espero que estos tres artículos os sirvan para conocer qué recursos disponemos desde Velneo para conectar nuestros datos con aplicaciones externas. Desde lo más nativo como las funciones remotas, pasando por módulos específicos como el driver ODBC, hasta llegar a conectar con el mundo web mediante APIs y servidores avanzados como Apache.

Seguiremos explorando nuevas herramientas que nos permitan extender las posibilidades de nuestras aplicaciones Velneo. No os lo perdáis, os espero.


Artículos relacionados:
Funciones remotas, driver ODBC y procesos web (I)
Funciones remotas, driver ODBC y procesos web (II)

Velneo es el entorno ágil para el desarrollo
de aplicaciones empresariales

DESCARGAR VELNEO

4 Responses to "Funciones remotas, driver ODBC y procesos web (III)"
  1. [N2] matcas dice:

    Muy buen articulo. Sería bueno que al final de cada articulo pusieran los enlaces a las anteriores partes para asi tenerlo completo de forma fácil.
    Gracias

  2. [N4] Juan dice:

    Genial articulo muy bueno, una duda:
    «La recomendación de Velneo es instalar una combinación de Apache/vModApache única por cada instancia que vayamos a usar con la aplicación externa web.»
    Osea asi instale otros apache en otros puertos, seguirian usando el mismo vmodapache y eso no es recomendable tampoco.

    • [N3] pacosatu dice:

      Hola Juan.

      Gracias por tu opinión.

      En cuanto a la arquitectura recomendada de Apache/vModApache, respecto a las Instancias de vServer, siempre estará determinada por nuestras necesidades y la carga que se vaya a soportar.
      Lo más adecuado es consultar a Velneo donde nos aconsejarán la configuración más óptima.

      En cuanto a tener 2 o más Apaches en puertos distintos y usando el mismo vModApache, no presenta problemas de carga, ya que cada módulo vModApache se carga en la memoria del servidor Apache y consumirá un enganche independiente, sin interferir entre ellos.

      Saludos
      Paco Satué

Deja un comentario

Esta web utiliza cookies. Si continúa navegando acepta dichas cookies y nuestra política de cookies. Gracias. ACEPTAR

Aviso de cookies