* ---
* 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
19 comentarios:
Saludos
Excelente, me sirvio mucho. En base a esta explicación desarrolle algo parecido para consultar nombres en base al numero de RUC, aquí en Ecuador.
El ejemplo funciona bien, pero cuendo usas https simplemente no conecta.
Si conoces algo del porque? te lo agradecieria enormemente.
Saludos
Necesita tener certificados válidos de ssl, si la conexión no es segura marcará esos errores.
Saludos.
Te felicito y te agradezco mucho, me sirvio.
Pero talvez me podrias ayudar.
En algunas ocasiones me devuelve: Código de excepción OLE IDispatch 0 de msxml6.dll: The operation timed out
Yo esperaba que los errores los pasara a catch
Alguna sugerencia para saber que diga cual es el error?
He buscado mucho para resolverlo.
Estoy usando el modo sincronico.
Buen dia.
Que tal Carlos.
Puedes controlar el tiempo de timeout agregando el siguiente código:
oHTTP = CREATEOBJECT('Msxml2.ServerXMLHTTP.6.0')
lResolve = 5 * 1000
lConnect = 5 * 1000
lSend = 40 * 1000
lReceive = 40 * 1000
oHTTP.setTimeouts(lResolve, lConnect, lSend, lReceive)
oHTTP.OPEN("POST", pURL_WSDL, .F.)
Ojo, debes establecer los tiempos antes de realizar el Open al objeto.
Saludos.
Hola! Aunque tengo el certificado instalado en la maquina me da error de certificado incorrecto o invalido.
Probé con este código que adjunto ya me pide el certificado antes de ejecutar el servicio con lo cual ya no me da error de certificado pero no devuelve datos. me devuelve toda la estructura del servicio, como sino reconociera el método que quiero ejecutar que se llama ECO.
oHTTP=Create('MSXML2.XMLHTTP')
oHTTP.Open("POST", "https://www.certificados.aduana.xxx/ws2/autos?wsdl", .F.)
oHTTP.setRequestHeader("Content-Type", "text/xml;charset=UTF-8")
oHTTP.setRequestHeader("SOAPAction",'https://www.certificados.xxx/ws2/autos#eco")
=MESSAGEBOX( pFileRequest)
*oHTTP.SEND(pFileRequest)
oHTTP.SEND()
Por favor agradezco cualquier ayuda que me puedan dar.
Gracias!
Hola! Probe los ejemplos citados y como mi servicio requiere un certificado SSL me daba error de certificado invalido. A pesar de que el certificado esta instalado en mi PC y con soapUI me responde bien el servicio.
Probe este ejemplo que adjunto y al probarlo me pide el certificado antes de ejcutarse y luego ya no da error de certificado, pero no me devuelbe datos. Devuelve la estructura completa del sergicio como si pudieras la url del servicio en el navegador.
El metodo que quiero ejecutar se llama “eco” y me parece que no esta tomado el metodo a ejecutar y por eso contesta con la estructura del servicio.
Adjunto el ejemplo a ver si me pueden ayudar:
TEXT TO sXMLRequest TEXTMERGE NOSHOW
30-66298283-7
123
ENDTEXT
pXMLResponse = ADDBS(SYS(2023)) + SYS(2015) + [.xml]
pFileRequest = sXMLRequest
TRY
oHTTP=Create('MSXML2.XMLHTTP')
oHTTP.Open("POST", "https://www.certificados.aduana.xxx/ws2/autos?wsdl", .F.)
oHTTP.setRequestHeader("Content-Type", "text/xml;charset=UTF-8")
oHTTP.setRequestHeader("SOAPAction", "https://www.certificados.aduana.dnrpa.gov.ar/ws2/autos#eco")
=MESSAGEBOX( pFileRequest)
*oHTTP.SEND(pFileRequest)
oHTTP.SEND()
Supongo que la url del web service es:
https://www.certificados.aduana.dnrpa.gov.ar/ws2/autos?wsdl
Ésta marca error de certificado (NET::ERR_CERT_AUTHORITY_INVALID), como lo comentaba anteriormente, el certificado SSL debe ser válido, no basta con instalarlo manualmente en la computadora.
Tendria que reportarlo al dueño del wsdl para que corrija ésta situación.
Saludos.
Gracias por responder.
El certificado es válido. De hecho si lo ejecuto con el soapPUI funciona perfecto.
Estoy hablando con los creadores del servicio y en otros lenguajes la invocación les funciona bien. Por eso pienso que es un tema de mi código VFP.
Si tenes algún ejemplo de la invocación de un servicio con SSL te lo agradecería mucho. Quiza viendo el ejemplo veo mi error.
No es válido, a mi me regresa ese error en mi navegador (NET::ERR_CERT_AUTHORITY_INVALID) , no hay problema en tu código, el problema es que al usar el objeto XMLHTTP, lo cual está documentado:
https://support.microsoft.com/en-us/kb/290761
"The ServerXMLHTTP and XMLHTTP components have limited HTTPS support"
Aunque, existen algunas opciones, que en lo particular no he usado, puedes probarlas:
https://msdn.microsoft.com/en-us/library/ms763811(v=vs.85).aspx
Podria ser: SXH_OPTION_IGNORE_SERVER_SSL_CERT_ERROR_FLAGS
Saludos.
Gracias por tu gran aporte.
yo apenas comienzo con esto, tengo los esquemas(xsd) y los archivos wsdl pero no se como usar estos, podrias apoyar en esto
Después de tanto y hacer varias pruebas encontré una forma de conectar con https desde VFP vía WebService, lo único que cambie fue la forma de crear la conexión:
oHTTP = CREATEOBJECT("Microsoft.XMLHTTP")
oHTTP.OPEN("POST", pURL_WSDL, .F.)
oHTTP.SEND(pFileRequest)
pURL_WSDL es la URL del webservice con https
pFileRequest es la respuesta recibida por la consulta al webservice
Saludos
Roberto Yuniz
Larga VIDA al zorro, una consulta copie tu ejemplo y lo he corrido pero me sale la pantalla con los datos vacios, por si acaso he cambiado las locaciones (lima, peru), pero nada no me muestra info. hay algo mal o es que despues de tantos años ya no esta activa la direccion que estas consultando, gracias por tu respuesta.
Larga VIDA al zorro, una consulta copie tu ejemplo y lo he corrido pero me sale la pantalla con los datos vacios, por si acaso he cambiado las locaciones (lima, peru), pero nada no me muestra info. hay algo mal o es que despues de tantos años ya no esta activa la direccion que estas consultando, gracias por tu respuesta.
La verdad, no sé si aún funcione el Servicio, puedes probar aquí:
http://www.webservicex.net/globalweather.asmx?op=GetWeather
Saludos.
Las imagenes ya no existen, puedes actualizar este post?
El WebService http://www.webservicex.net/globalweather.asmx?WSDL
Ya no existe:
{"code":"PAGE_NOT_FOUND","message":"Page not found"}
Por lo que el ejemplo ya no es válido; prepararé una actualización de ésta entrada de blog.
Saludos.
Gracias a que Xuan Madrid me pasó un PDF con las imágenes las he actualizado.
¡Saludos!
He actualizado el código, con ejemplo nuevo y un webservice propio (solo de ejemplo)
Saludos a todos.
Publicar un comentario