martes, noviembre 22, 2011

Consumir cualquier Web Service con Visual FoxPRO


* ---
* Actualización 2018-16-20: Se actualiza el ejemplo, código e imágenes.
* ---

Que tal Colegas.

Este artículo es para mostrarles como podemos invocar cualquier Web Service desde Visual FoxPRO.

Primero quisiera comentar, que NO soy un experto en ésto de los WEB service's, pero he tomado algo de experiencia en el uso de los mismos. En éste artículo trataré de explicarlo con palabras simples.

Es importante leer al respecto, siempre es sencillo encontrar información en Wikipedia.

Vamos comenzando por la lógica de "ejecución" de un web service:

1. Creamos la petición de ejecución de una función del Web Service, ésto se llama XML REQUEST.
2. Enviamos el REQUEST al web service, la cual es una URL que termina con wsdl.
3. Obtenemos un XML RESPONSE, que es el resultado de haber invocado la función específica del WS.

Vamos por partes:

Para saber como es el REQUEST, utilizo una aplicación Open Source llamada soapUI, la cual pueden descargar desde: http://www.soapui.org/

Lo descargamos, al abrir el soapUI vemos ésto:




La URL del web service es: http://www.gencfd.com/wsEjemplo/index.php?wsdl




Con ésto, soapUI interperta las funciones que existen en ése WebService, y nos crea un ejemplo de REQUEST por cada una de ellas, en éste caso observaremos como el REQUEST de la función SUMA:



Hasta aquí, tenemos cubierto el paso 1 de la ejecución de un Web Service, el dos es simple.. damos clic en el botón de "PLAY" y se cumple el segundo paso, al mismo tiempo que obtenermos el XML RESPONSE:



Bien, como hacemos ésto con FoxPRO:

Al conocer la estructura del XML Request, pues lo creamos con FoxPRO, usen el método que a ustedes les parezca mejor, en lo personal me agrada la utileria Free de Chilkat, pero para el ejemplo crearé el Request con TEXT.. ENDTEXT

He creado una clase para consumir WS, en ella muestro el ejemplo anterior en código, está comentada por lo que creo no es necesario hablar más al respecto.

Va el código:

oWS = CREATEOBJECT( "VFP_WebService","http://www.gencfd.com/wsEjemplo/index.php?wsdl") 
lcRespuesta = oWS.Suma(10,8)
IF oWS.iStatus != 0
    MESSAGEBOX(oWS.sError,16,"Error al ejecutar WS")
ELSE
    MESSAGEBOX(lcRespuesta,64,"EjecutandoWS desde VFP - PortalFOX")
ENDIF  


* -- Ahora usando el ActiveX de Chilkat --
* -- **http://www.chilkatsoft.com/ChilkatXml.asp** --
lcRespuesta = oWS.Suma_Chilkat(10,8)
IF oWS.iStatus != 0
    MESSAGEBOX(oWS.sError,16,"Error al ejecutar WS")
ELSE
    MESSAGEBOX(lcRespuesta,64,"EjecutandoWS desde VFP usando Chilkat - PortalFOX")
ENDIF  


DEFINE CLASS VFP_WebService AS CUSTOM

    * --- Definimos las propiedades ---
    sError = ""
    iStatus = 0 
    sURL_WS = ""

    * --- Definimos la función del WebService ---
    FUNCTION Suma(tnI, tnJ)
        * --- Paso 1. Creo el XML Request ---
        lcXMLRequest = this.CreaRequest(tnI, tnJ)
        lsXMLResponse = ADDBS(SYS(2023)) + SYS(2015) + [.xml]
        * --- Paso 2. Ejecuto el WS | Paso 3. Obtengo el Response ---
        this.iStatus =  this.EjecutaWS( this.sURL_WS, lcXMLRequest , lsXMLResponse )
        IF this.iStatus != 0  && Ocurrió un error el cual está especificado en sError.
            RETURN ""
        ENDIF 
        lcXMLResponse = FILETOSTR(lsXMLResponse)
        * --- Parseamos el XML Response ---
        * --- Para el ejemplo está así, manejando texto, ustedes deben manejar XML (falta de tiempo, perdón) ---
        lcRespuestaWS = STREXTRACT(lcXMLResponse ,[<res xsi:type="xsd:integer">],[</res>])
        this.borraArchivo(lsXMLResponse)
        RETURN lcRespuestaWS 
    ENDFUNC 

    *---------------------------------------------------
    FUNCTION EjecutaWS(tcURL_WSDL, tcFileRequest , tsFileResponse )
    *---------------------------------------------------
        TRY 
            oHTTP = CREATEOBJECT('Msxml2.ServerXMLHTTP.6.0')
            oHTTP.OPEN("POST", tcURL_WSDL, .F.)
            oHTTP.setRequestHeader("User-Agent", "EjecutandoWS desde VFP - PortalFOX")
            oHTTP.setRequestHeader("Content-Type", "text/xml;charset=utf-8")
            oHTTP.SEND(tcFileRequest)
        CATCH TO loErr
            this.sError = "Error: " + TRANSFORM(loErr.ErrorNo) +  " Mensaje: " + loErr.Message
            this.iStatus = -1      
        ENDTRY 
        IF this.iStatus != 0
            RETURN -1
        ENDIF 
        * --- Si el status es diferente a 200, ocurrió algún error de conectividad con el WS ---
        IF oHTTP.STATUS = 200
            lcRespuestaWS = oHTTP.responseText
            * --- Se genera el XML del response | Este es el paso 3!! ---
            STRTOFILE(STRCONV(lcRespuestaWS ,9),tsFileResponse)
            this.iStatus = 0
            this.sError = ""
            RETURN 0
        ELSE
            this.sError = "Error: No se logró la conexión con el Web Service."
            this.iStatus = -1
            RETURN -1
        ENDIF
    ENDFUNC 
    *---------------------------------------------------

    *---------------------------------------------------
    FUNCTION CreaRequest(tnI, tnJ)
    *---------------------------------------------------
        TEXT TO lcXMLRequest TEXTMERGE PRETEXT 7 NOSHOW 
            <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:suma">
                <soapenv:Header/>
                <soapenv:Body>
                    <urn:suma soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
                        <i xsi:type="xsd:integer"><<tnI>></i>
                        <j xsi:type="xsd:integer"><<tnJ>></j>
                    </urn:suma>
                </soapenv:Body>
            </soapenv:Envelope>
        ENDTEXT 
        RETURN lcXMLRequest 
    ENDFUNC  
    *---------------------------------------------------
    
    *---------------------------------------------------
    FUNCTION CreaRequest_Chilkat(tnI, tnJ)
    *---------------------------------------------------
loXml = CreateObject('Chilkat.Xml')
loXml.Encoding = "UTF-8"
loXml.Tag = "soapenv:Envelope"
        loXml.AddAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")
        loXml.AddAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema")
        loXml.AddAttribute("xmlns:soapenv","http://schemas.xmlsoap.org/soap/envelope/")
        loXml.AddAttribute("xmlns:urn","urn:suma")
        loXml = loXml.NewChild("soapenv:Header","")
loXml.GetRoot2()
loXml = loXml.NewChild("soapenv:Body","")
        loXml = loXml.NewChild("urn:suma","")
        loXml.AddAttribute("soapenv:encodingStyle","http://schemas.xmlsoap.org/soap/encoding/")
        loXml = loXml.NewChild("i",tnI)
        loXml.AddAttribute("xsi:type","xsd:integer")
        loXml.GetParent2()
        loXml = loXml.NewChild("j",tnJ)
        loXml.AddAttribute("xsi:type","xsd:integer")
        loXml.GetRoot2()
        RETURN loXML.getxml()
    ENDFUNC  
*---------------------------------------------------
FUNCTION GetChilkatPath
*---------------------------------------------------
* Esta función obtiene el resultado de evaluar
* un Chilkat Xml Path. Para mas referencia
* revisar: http://www.chilkatsoft.com/refdoc/xChilkatXmlRef.html
LPARAMETERS etPath,spXml
loXml = CreateObject('Chilkat.Xml')
lnSuccess = loXml.LoadXmlFile(spXML)
IF (lnSuccess = 0) THEN
    this.sError = "Error de parse en el archivo xml"+ loXml.LastErrorText
    this.iStatus = -999
RETURN ""     
ENDIF
loXml.GetRoot2()
lcTemp = loXml.ChilkatPath(etPath)
loXML = .null.
RELEASE loXML
RETURN IIF(ISNULL(lcTemp),"",lcTemp)
ENDFUNC 
*---------------------------------------------------
    *---------------------------------------------------
    FUNCTION Suma_Chilkat(tnI, tnJ)
        * --- Paso 1. Creo el XML Request ---
        lcXMLRequest = this.CreaRequest_Chilkat(tnI, tnJ)
        lsXMLResponse = ADDBS(SYS(2023)) + SYS(2015) + [.xml]
        * --- Paso 2. Ejecuto el WS | Paso 3. Obtengo el Response ---
        this.iStatus =  this.EjecutaWS( this.sURL_WS, lcXMLRequest , lsXMLResponse )
        IF this.iStatus != 0  && Ocurrió un error el cual está especificado en sError.
            RETURN ""
        ENDIF 
        lcRespuestaWS = this.GetChilkatPath("/C/SOAP-ENV:Envelope|SOAP-ENV:Body|ns1:sumaResponse|res|*",lsXMLResponse )
        this.borraArchivo(lsXMLResponse )
        RETURN lcRespuestaWS 
    ENDFUNC 

    *---------------------------------------------------
    FUNCTION BorraArchivo(tsFile)
    *---------------------------------------------------
        IF FILE(tsFile)
            DELETE FILE (tsFile)
        ENDIF 
    ENDFUNC 
    *---------------------------------------------------

    *---------------------------------------------------
    * Evento constructor
    PROCEDURE Init
    *---------------------------------------------------
        LPARAMETERS tcURLWS
        this.sURL_WS = tcURLWS
        this.iStatus = 0
        this.sError = ""
    ENDPROC
    *---------------------------------------------------
ENDDEFINE  

Pueden descargar el código desde aquí.

Suerte con sus Web Services!!

Baltazar Moreno
http://disxii.com
VFP9SP2 - Win7
Guadalajara, Jalisco, México

miércoles, octubre 19, 2011