domingo, 24 de marzo de 2019

Entendiendo el objeto Microsoft.xmlHTTP

Microsoft.xmlHTTP (Extensible MarkUp Language / Hyper Text Transfer Protocol) es un objeto de la familia COM ActiveX que Microsft originalmente provee como acceso del lado del cliente a documentos XML alojados en servidores remotos. La comunicación se logra a traves del protocolo HTTP y consta de una pequeña API que permite realizar peticiones HTTP y obtener los resultados en distintos formatos (XML, HTML, JSON, BINARY DATA, ETC).

Sus atributos principales son: (extraído de Wikipedia)


  • readyState: devuelve información del estado del objeto (0 = sin inicializar, 1 = abierto, 2 = cabeceras recibidas, 3 = cargando, 4 completado).
  • responseBody: devuelve la respuesta como un array de bytes.
  • responseText: devuelve la respuesta como una cadena.
  • responseXML: devuelve la respuesta como XML.
  • status: devuelve el estado como un número. Ejemplo: 404 para "Not Found"
  • statusText: devuelve el estado como una cadena. Ejemplo: "Not Found"

Lista de métodos:  (extraído de Wikipedia)

  • abort(): simplemente cancela la petición en curso. En un caso de uso pudieramos tener una ventana donde la solicitud se esté procesando y a traves de un commandButton podemos brindarle la opción al usuario de poder cancelar la petición (mala conexión al internet, fallas en el sistema operativo, latencia en la red, etc). Al dar click en cancelar, utilizamos este método y finalmente presentaríamos un mensaje de "proceso cancelado por el usuario".
  • getAllResponseHeader(): devuelve el conjunto de cabeceras HTTP como una cadena. Esta información sería las cabeceras definidas por el servidor y no las definidas en el cliente.
  • getResonseHeader(cHeaderName): devuelve el valor de la cabecera HTTP especificada.
  • open(cMethodName, cURL [,asyncrono [, nombre usuario [, clave]]]): abre la comunicación con el servidor. Se especifica el Método y la URL; los demás argumentos son opcionales.
  • send([datos]): envía la petición.
  • setRequestHeader(): añade un KVP (key, value, pair) a la cabecera HTTP a enviar.

Aplicación en Visual FoxPro


Para aplicar el objeto XMLHTTP dentro de Visual Foxpro 9.0 simplemente hacemos uso del comando CREATEOBJECT

Veamos algunos ejemplos con lo descrito anteriormente:


*-- Declarar la variable xmlHTTP [Opcional]

LOCAL xmlHTTP as "Microstft.XMLHTTP"

Aunque este paso es opcional debido a que VFP es debilmente tipado, siempre es bueno mantener las buenas practicas de programación y declarar las variables con su respectivo tipo antes de usarlas.


*-- Crear la instancia del objeto XMLHTTP
xmlHTTP = CREATEOBJECT("Microsoft.XMLHTTP")

En este paso ya tenemos una referencia al objeto XMLHTTP creada. (Estos pasos los puedes realizar en modo interactivo usando la consola de VFP)

*-- Enviando la peticion HTTP a un servidor remoto
xmlHttp.open("GET", "https://swapi.co/api/planets/1/")

Aquí usamos el método open para enviar la petición HTTP. En el ejemplo se especifica el verbo GET a la direccion del servidor https://swapi.co/api/planets/1/. La URL apunta a una API gratuita de Star Wars que sirve consumir recursos relacionados con la serie. En este caso estamos pidiendo el detalle del plateta cuyo identificador unico es el número 1.

?xmlHttp.readyState

Si en este momento consultamos el atributo readyState obtendríamos por pantalla un "1". Nuestro objeto nos está diciendo que la petición está abierta.

?xmlHttp.responseText


Si nos adelantamos a imprimir un responseText por pantalla entonces obtendríamos una excepción no controlada diciendo que la operación no esta disponible todavía. Esto se debe a que no hemos enviado la petición al servidor aún, solo quedamos en el método open que es distinto del método send.


xmlHTTP.send()
?xmlHttp.readyState


Si ejecutamos el método send del objeto xmlHTTP entonces ya estaríamos enviando la petición al servidor. Seguidamente si imprimimos el atributo readyState por pantalla entonces obtendríamos un "4" que significa completado.

NOTA: el atributo readyState no pasa del 1 al 4 directamente, en esa transición se encuentra el valor 3 que significa "cargando" y puede demorarse por motivos ajenos a nuestro código (latencia en la señal de internet, sin conexión de red, etc), por lo tanto siempre es recomendable evaluar su valor antes de pedir la respuesta dela petición.


?xmlHttp.status



Al imprimir por pantalla el atributo status (si todo ha marchado bien) vemos que nos muestra el valor "200" indicandonos que la respuesta HTTP del servidor es "OK"

Para mayor información acerca de los códigos de respuestas HTTP véase el siguiente link: https://es.wikipedia.org/wiki/Anexo:Códigos_de_estado_HTTP


?xmlHttp.statusText

El atributo statustext es la representación en cadena del atributo status, es decir, la descripción del valor numérico obtenido como respuesta al hacer uso del atributo status. (ver enlace anterior).


?xmlHttp.responseText


A estas alturas ya es posible obtener el contentido del atributo responseText, en este caso lo imprimimos por pantalla y obtendríamos la respuesta enviada desde el servidor. 




RELEASE xmlHTTP


Finalmente hay que liberar la variable de la memoria.

Un ejemplo controlado sería el siguiente:


CLEAR ALL
RELEASE ALL
*-- Declarar la variable de tipo XMLHTTP
LOCAL xmlHTTP As "Microsoft.XMLHTTP", ;
lcURL As String

*-- Definir las constantes para la evaluación de resultados.
#DEFINE HTTP_STATUS_OK        200
#DEFINE HTTP_COMPLETED        4
#DEFINE HTTP_OPEN             1
#DEFINE CR                    CHR(13)
#DEFINE MSGBOX_INFO           64
#DEFINE MSGBOX_WARNING        48
#DEFINE TYPE_OBJECT           "O"
#DEFINE TIME_OUT              3 && Tiempo de espera máximo para la respuesta.

lcURL = "https://swapi.co/api/planets/1/"

*-- Creamos la instancia del objeto XMLHTTP
xmlHTTP = CREATEOBJECT("Microsoft.XMLHTTP")

IF TYPE("xmlHTTP") <> TYPE_OBJECT
      WAIT "No se pudo crear el objeto (XMLHTTP)." WINDOW NOWAIT
      RETURN
ELSE &&TYPE("xmlHTTP") <> TYPE_OBJECT
ENDIF &&TYPE("xmlHTTP") <> TYPE_OBJECT

*-- Abrimos la conexión
xmlHTTP.open("GET", lcURL)

*-- Evaluamos el stado del objeto.
IF xmlHTTP.readyState <> HTTP_OPEN
      WAIT "No se pudo procesar su solicitud." WINDOW NOWAIT
      RETURN     
ELSE &&xmlHTTP.readyState <> HTTP_OPEN
ENDIF &&xmlHTTP.readyState <> HTTP_OPEN

*-- Enviamos la petición HTTP
xmlHTTP.send()

*-- Tenemos que esperar a que cambie el valor del atributo readyState.
*-- para ello utilizamos un bucle controlado con un máximo de 3 segundos. 
nSeg = SECONDS() + TIME_OUT
DO WHILE SECONDS() <= nSeg
      WAIT "Esperando respuesta del servidor, tiempo restante {" + STR(nSeg - SECONDS()) + "} Seg." WINDOW NOWAIT
      IF xmlHTTP.readyState <> HTTP_OPEN
            *-- Hubo una respuesta
            EXIT
      ELSE &&xmlHTTP.readyState <> HTTP_OPEN
      ENDIF &&xmlHTTP.readyState <> HTTP_OPEN
      WAIT CLEAR
ENDDO &&WHILE SECONDS() <= nSeg

*-- Evaluamos la respuesta.
IF xmlHTTP.readyState == HTTP_COMPLETED AND xmlHTTP.status == HTTP_STATUS_OK
      MESSAGEBOX("Solicitud procesada exitosamente." + CR + "La respuesta del servidor es: " + CR + xmlHTTP.responseText, MSGBOX_INFO, "Success"
ELSE &&xmlHTTP.readyState == HTTP_COMPLETED AND xmlHTTP.status == HTTP_STATUS_OK
      *-- Es recomendable encapsular el control de errores HTTP en una función.
      WAIT "No se pudo procesar su solicitud." WINDOW NOWAIT
ENDIF &&xmlHTTP.readyState == HTTP_COMPLETED AND xmlHTTP.status == HTTP_STATUS_OK

RELEASE xmlHTTP


Espero que el artículo les haya sido de utilidad.

Happy coding!!!