Invocar Web Services desde PL/SQL en Oracle

El problema

Muchas veces nos vemos en la necesidad de invocar un Web Service directamente desde la base de datos.

La utilización de Java dentro de la base de datos no siempre es una opción disponible, así que tuve que buscar una solución que sólo utilizara código PL/SQL.

La solución

Podemos utilizar el package UTL_HTTP para hacer los request SOAP directamente al servidor destino, esto si bien es más complicado que utilizar las clases proxy, nos evita tener que cargar Java en la base de datos.

La forma de invocar el package UTL_HTTP es la siguiente:

DECLARE
 
    req   UTL_HTTP.req := NULL;
    resp  UTL_HTTP.resp := NULL;
    respVal VARCHAR2(32000);
    reqXML VARCHAR2(32760);
 
BEGIN
 
/*Generamos un Request a la URL destino, el método debe ser POST */
    req := UTL_HTTP.begin_request('http://servidor/ConsultaClientes', 'POST');
 
/*Creamos un mensaje SOAP tal cual se define en el WSDL*/
 
reqXML := '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:m0="ConsultaClientes">
      <SOAP-ENV:Body>
            <m:ConsultaClientes xmlns:m="http://servidor/ConsultaClientes">
                  <m0:mensaje>
                        <m0:idcliente>C123452</m0:idcliente>
                  </m0:mensaje>
            </m:ConsultaClientes>
      </SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
 
/*El contenido que enviamos es XML: */
    UTL_HTTP.set_header(req, 'Content-Type', 'text/xml');
 
/*Establecemos el SOAPAction a invocar: */
    UTL_HTTP.set_header(req, 'SOAPAction', '"rpc/http://servidor/ConsultaClientes"');
 
/*Indicamos en el header el tamańo del mensaje enviado: */
    UTL_HTTP.set_header(req, 'Content-Length', LENGTH(xml));
 
/*Escribimos el body del request */
    UTL_HTTP.write_text(req, xml);
 
/*Obtenemos la respuesta */
    resp := UTL_HTTP.get_response(req);
 
/*Cargamos en la variable respVal la devolución del servidor */
    UTL_HTTP.read_text(resp, respVal);
 
/*Finalizamos la conexión HTTP */
    UTL_HTTP.end_response(resp);
 
EXCEPTION
    WHEN UTL_HTTP.end_of_body THEN
      UTL_HTTP.end_response(resp);
END;
 
/

Conclusion

De esta manera rápida pudimos invocar un Web Service remoto utilizando PL/SQL, a partir de allí es posible convertir lo recibido en la variable respVal a un XMLTYPE para un mejor manejo.

Espero que les haya servido!!

Esta entrada fue publicada en Oracle y etiquetada , . Guarda el enlace permanente.

53 respuestas a Invocar Web Services desde PL/SQL en Oracle

  1. hernando dijo:

    Es posible implementar este codigo sin estar conectado a la base de datos?, debido a que debo usarlo cuando realice un login failure, es decir, el usuario esté bloqueado, o la cuenta expirada, etc… por lo tanto no habrá coneccion a la base de datos…

    muchas gracias!

  2. Hi! There are certainly a lot of details like that to take into consideration. That is a great point to bring up. I offer the thoughts above as general inspiration but clearly there are questions like the one you bring up where the most important thing will be working in honest good faith. I don?t know if best practices have emerged around things like that, but I am sure that your job is clearly identified as a fair game. Both boys and girls feel the impact of just a moment’s pleasure, for the rest of their lives.

  3. Jairo dijo:

    CREATE OR REPLACE
    FUNCTION SWEJEMPLO RETURN VARCHAR2 AS
    BEGIN
    DECLARE

    req UTL_HTTP.req := NULL;
    resp UTL_HTTP.resp := NULL;
    respVal VARCHAR2(32000);
    reqXML VARCHAR2(32760);

    BEGIN

    /*Generamos un Request a la URL destino, el método debe ser POST */
    req := UTL_HTTP.begin_request(‘http://(ip)/su/wsvad/wsad.asmx', ‘POST’);

    /*Creamos un mensaje SOAP tal cual se define en el WSDL*/

    reqXML := ‘

    string
    string

    ‘;

    /*El contenido que enviamos es XML: */
    UTL_HTTP.set_header(req, ‘Content-Type’, ‘text/xml’);

    /*Establecemos el SOAPAction a invocar: */
    UTL_HTTP.set_header(req, ‘SOAPAction’, ‘”http://www.um.edu.co/isVad”‘);

    /*Indicamos en el header el tamańo del mensaje enviado: */
    UTL_HTTP.set_header(req, ‘Content-Length’, LENGTH(reqXML));

    /*Escribimos el body del request */
    UTL_HTTP.write_text(req, reqXML);

    /*Obtenemos la respuesta */
    resp := UTL_HTTP.get_response(req);

    /*Cargamos en la variable respVal la devolución del servidor */
    UTL_HTTP.read_text(resp, respVal);

    /*Finalizamos la conexión HTTP */
    UTL_HTTP.end_response(resp);

    return respVal;

    EXCEPTION
    WHEN UTL_HTTP.end_of_body THEN
    UTL_HTTP.end_response(resp);
    return ‘nada’;
    END;

    END SWEJEMPLO;

    me sale este error ayuda

    ORA-06502: PL/SQL: error : buffer de cadenas de caracteres demasiado pequeño numérico o de valor
    ORA-06512: en línea 5
    Process exited.

  4. fernando dijo:

    hola queria saber si hay algun codigo ejemplo para invocar un web service desde un pl sql en oracle 8i, Gracias!!!

  5. Josechinese dijo:

    Estaba probando el código y tengo un problema, imprimo el http_resp.status_code y me envía el status 401 que significa UNAUTHORIZED,

    ¿alguien sabe como solucionar este problema?

    que permisos requiere?… xq el servicio que trato de consumir esta abierto para todo el mundo… es del banco, que te envia el tipo de cambio del dolar.

    aqui esta la especificacion del servicio:

    http://www.banguat.gob.gt/variables/ws/TipoCambio.asmx?op=TipoCambioDia

    cualquier ayuda se agradece… :D

  6. bpfs10 dijo:

    Tengo un problema cuando invoco al web service y este se tarda en responder me gustaria saber si hay alguna manera de saber si llegó la peticicion (el request) o lo tiene en espera procesando otros.
    Necesito algo para evaluar si debo o no, volver a enviar la petición.

  7. Mariano dijo:

    Me ha sido de mucha utilidad esta información. Tengo ahora el siguiente problema, el WS a consumir tiene certificado de seguridad (es un https) y me da los siguientes errores al levantar la línea del UTL_HTTP.begin_request:

    ORA-29273: fallo en la solicitud HTTP
    ORA-29024: Fallo de validación de certificado
    ORA-06512: en linea 3

    Gracias y saludos

  8. Freddy dijo:

    Mil disculpas solo quiero saber si me pueden dar una mano con este problema

    SOAP-ENV:Client
    No Deserializer found to deserialize a 'GetFullName:m0:mensaje' using encoding style 'null'. [java.lang.IllegalArgumentException]
    /WsSample2-WsSample2-context-root/MyWebService1

  9. Pedro dijo:

    He seguido el ejemplo y obtengo el siguiente error. Yo obtengo el siguiente error:

    ORA-12541: TNS:no listener
    ORA-06512: at line 11
    29273. 00000 – “HTTP request failed”

    cual podría ser el problema, la verdad cualquier tipo de ayuda me viene muy útil.

    saludos

  10. milfar dijo:

    señores me a salido el siguiente error. si alguien pude y quiere ayudar se lo agradesco

    [1]: (Error): ORA-06550: line 33, column 56: PLS-00201: identifier ‘XML’ must be declared ORA-06550: line 33, column 4: PL/SQL: Statement ignored ORA-06550: line 35, column 30: PLS-00201: identifier ‘XML’ must be declared ORA-06550: line 35, column 4: PL/SQL: Statement ignored

    • Sebas dijo:

      Milfar, fijate que hay un error en la linea:

      UTL_HTTP.set_header(req, ‘Content-Length’, LENGTH(xml));

      Debería ser:

      UTL_HTTP.set_header(req, ‘Content-Length’, LENGTH(reqXML));

      Y en vez de:

      UTL_HTTP.write_text(req, xml);

      Debería ser:

      UTL_HTTP.write_text(req, reqXML);

      Espero que ese sea tu error, de paso aprovecho y corrijo el post así queda el código corregido.

      Saludos!!

  11. Kramirez dijo:

    Tengo un problema al ejecutar el consumo de un WS me devuelve el error siguiente

    999Referencia a objeto no establecida como instancia de un objeto.

    Cual consideran puede ser el problema ???

  12. Faustino dijo:

    Hola, dejo esto por si alguien puede ayudarme.

    Tengo un caso donde invoco un WS desde el browser y me responde esto:


    00Exitosa3900000046419535899999100.00DOP’

    y cuando invoco el WS desde mi pl/sql entonces me retorna esto:


    <?xml version=”1.0″ encoding=”utf-8″?><DetalleReciboPago version=”1.00″><CodigoMensaje>00</CodigoMensaje><DescripcionMensaje>Exitosa</DescripcionMensaje><NumeroReferencia>39</NumeroReferencia><NumeroAutorizacion>000000032499675</NumeroAutorizacion><IDTransaccionBanco>99999</IDTransaccionBanco><ValorPagado>100.00</ValorPagado><Moneda>DOP</Moneda></DetalleReciboPago>’

    Puede alguien decirme a que se debe esto?

    Saludos.

  13. Andy dijo:

    Todo esto ya lo hemos vivido y solucionado. El problema que tenemos ahora es que al intentar usar un WS en una url segura (https) necesitamos establecer los certificados de acceso mediante el OWM pero esta herramienta no nos permite realizar ninguna acción (se abre e intenta crear, abrir, etc. ….y se queda pegado).
    Cualquier comentario se agradece !

  14. Kamilo dijo:

    ORA-12545

    buen dia ejecute el procedimiento pero me genera el siguiente error.
    ORA-29273: HTTP request failed
    ORA-06512: at “SYS.UTL_HTTP”, line 1029
    ORA-12545: Connect failed because target host or object does not exist
    ORA-06512: at line 13

    • Sebas dijo:

      @Kamilo, aparentemente no tenés acceso a ese host destino que intentas invocar.

      El servidor destino lo ponés en la siguiente linea:

       
      req := UTL_HTTP.begin_request('<a href="http://servidor/ConsultaClientes&#039;" rel="nofollow">http://servidor/ConsultaClientes&#039;</a>, 'POST');
      

      Donde “servidor” debería ser el nombre de tu server destino, y “/ConsultaClientes” la dirección final donde se aloje el Web Service de destino.

      Si eso lo tenés bien configurado, entonces debería probar si el servidor dónde se aloja la base de datos tiene conectividad con el servidor destino.

      Saludos!!

  15. Juliusmanx dijo:

    Volviendo al bonito ejemplo

    He desarrollado un Servicio Web(WS) en Visual Studio 2005, y tengo la base de datos en Oracle 10g Express, para pruebas…

    Mi WS se llama http://localhost/ews_ora/
    Que posee como 10 metodos
    y el metodo que seo invocar es… http://localhost/ews_ora/Service.asmx?op=Peticion_XML
    Peticion_XML recibe 3 argumento tipo String y devuelve solo 1 tipo String siempre.

    segun el ejemplo de esta pagina tendria que poner mis 3 argumento en: reqXML

    • Sebas dijo:

      @Juliusmanx, a ver si entiendo la idea, ¿vos querés invocar desde la base de datos ese Web Service?

      Entonces para utilizar este ejemplo, deberías como primer medida obtener el XML de ejemplo necesario para invocar ese WS.

      Una vez que tenés ese XML de request, lo que debés hacer es completarlo con los parámetros que precises.

      Saludos!!

  16. Sebas dijo:

    @Faustino, para escribir un “xml” en los comentarios podés probar con poner:

    < tag >valor< tag >

    Los espacios permiten que el comentario pase sin ser analizado como HTML.

    Saludos!!

    • Faustino dijo:

      @Sebas, Hola Sebas, aqui te envio el mensaje xml que necesito parsear y poner el calor de los campos en variables tipo Varchar2.

      00
      Consulta realizada con exito. Edenorte Dominicana.
      0060000000
      Erika Zabaleta
      Cristian test 15 4
      Tester 01
      100000000000

      20080423
      000800000006
      DOP
      5000.0
      DG
      CLIENTE NUEVO

      20080423
      000800000006
      DOP
      5000.0
      DG
      CLIENTE NUEVO

      Si te fijas tengo primero 6 campos, y el ultimo que es tiene un sub xml, que tiene unas estructuras que hay que capturar esos calores tambien en variables Varchar2

  17. Faustino dijo:

    Hola Sebas.

    Como puedo convertir la variable respVal en un XMLType?

    • Juan dijo:

      @Faustino, Lo tenés que hacer de la siguiente manera:

      resp := UTL_HTTP.get_response(req);

      UTL_HTTP.read_text(resp, val);

      UTL_HTTP.end_response(resp);

      val_xml := xmltype(val);

      Es decir, utilizando la función xmltype. Una vez que lo tengas ahí dentro podés usar val_xml.extract para sacar los datos.

      Espero te sirva!

      • Faustino dijo:

        @Juan, gracias por la ayuda.

        Mi variable xmltype esta declarada v_resp xmltype; y la cariable donde captura la respuesta del web service es respVal varchar2(32000);

        cuando agrego la linea v_resp := xmltype(respVal); y luego le doy a compilar mi stored procedure, el pl de freeza y no me responde.

        • Juan dijo:

          @Faustino, a lo mejor tenés un problema con el formato de la respuesta y no la está pudiendo transformar a xmltype, probá hacer:

          SELECT xmltype(‘todo el código xml’) FROM DUAL;

          y fijate qué sucede.

          • Faustino dijo:

            @Juan, Perfecto Juan, me di cuenta que tenia campos del xml mal nombrados. Al parecer esto generaba algún conflicto. Ya me compilo bien.

            Por ultimo y espero no molestarte más hermano, ya tengo mi variable xmltype llena…. como puedo esos campos de esa estructura xml que tengo ahi, parecearlos para asignar esos valores a diferentes variables de salida de mi procedure?

            Gracias anticipadas.

          • Juan dijo:

            @Faustino,

            Para eso te sugiero que investigues un poco de XPath, que es lenguaje utilizado para parsear archivos XML. Si querés fijate en este link:

            http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96616/arxml24.htm

            Un ejemplo sería, si tenés un XML con la siguiente estructura:

            <xml><tag>Valor del Tag</tag></xml>

            Para sacar el valor podrías utilizar:

            v_resp.EXTRACT('/xml/tag').getStringVal(); (con PATH absoluto)

            ó

            v_resp.EXTRACT('//tag').getStringVal(); (con PATH relativo)

            Si tuvieras atributos se agrega un @ adelante, por ejemplo:

            v_resp.EXTRACT('//@nombreAttr').getStringVal();

            Suerte!

          • Faustino dijo:

            @Juan, Definitivamente eres un conocedor de este tema. Yo por mi parte es primera vez que trabajo con XML en mi corta vida de programador. jejeje. no tengo como agradecerte. el getStringVal() me a funsionado bien.

            El unico inconveniente es que por ejemplo cuando uso esta linea

            V_RESPONSE_CODE_OUT := v_resp.extract(‘//codigoRespuesta’).getStringVal();

            la variable V_RESPONSE_CODE_OUT me la llena con 00, a mi me interesa que solo la llene con el 00 que es el valor del campo en la estructura xml.

            ¿es eso posible?

          • jic dijo:

            @Faustino, entiendo que lo que querés es el valor de un atributo y no del tag… si es así ponele un @ adelante:

            v_resp.extract(’//@codigoRespuesta’).getStringVal();

            Sino investigate ese link que te pasé para encontrar alguna forma de parsearlo y que te sirva.

          • Sebas dijo:

            @Faustino, ¿podrías incluir una parte del XML que tienes que parsear?

            De paso, pasanos la declaración de “V_RESPONSE_CODE_OUT” así podemos ver el tipo de dato que estas utilizando.

            Si vos tenés más de un tag llamado “codigoRespuesta” y utilizás el XPath en modo búsqueda, podés estar devolviendo el array de la respuesta, por ejemplo:

             
            <xml>
            	<mensaje>
            		<codigoRespuesta>00</codigoRespuesta>
            	</mensaje>
            	<mensaje>
            		<codigoRespuesta>00</codigoRespuesta>
            	</mensaje>
            </xml>
             

            En ese XML estarías encontrando más de una respuesta para “//codigoRespuesta”.

            Saludos!

          • Faustino dijo:

            @Sebas, hola.

            En este caso no sucedera porque me responde un xml con tres campos unicos. y las variables en que quiero almanecar el contenido de esos campos son declaradas Varchar2, tal es el caso de V_RESPONSE_CODE_OUT.

            para este xml:

            00
            Consulta exitosa

            quiero almacenar solo el calor 00 en V_RESPONSE_CODE_OUT, sindo esta una variable varchar2. Hasta ahora estoy logrando almacenar en la V_RESPONDE_CODE_OUT toda la linea, es decir, 00. pero solo quiero que sea 00.

            Gracias de antemano

          • jic dijo:

            @Faustino, probaste con el @ que te dije? Seguramente es un atributo. Sino adjuntamos todo el código xml para que podamos verlo.

            Saludos,
            Juan

          • Faustino dijo:

            seria posible que me pasen un correo donde les pueda mandar todo el xml que estoy recibiendo, y como quiero capturar cada valor de los campos del xml en variables tipo varchar2.

            mi correo es faustino_a80@hotmail.com

            Esto porque no se como escribir el xml aqui sin que salga incompleto.

          • Faustino dijo:

            @jic, Juan, como estas?

            Te he escrito correos respondiendo el mensaje que me enviaras y no he recibido respuestas. Nada, solo queria saber si aun estas en la disposicion de ayudarme. Saludos hermano, y en definitiva gracias por toda la ayuda que me has brindado hasta ahora.

  18. Sebas dijo:

    Juan, que error te manda? podrías explicar un poco mas como estas armando la variable XML?

    Saludos!!

  19. Juan dijo:

    Help !!!..

    Tengo mi archivo WSDL.. ahora quiero publicarlo desde un PL/SQL,
    Quiero echar andar este ejemplo… me manda un error en la linea sig:

    UTL_HTTP.set_header(req, ‘Content-Length’, LENGTH(xml));

    como lo hago funcionar para poder replicar lo mismo en mi caso.

    Saludos

  20. RLeon dijo:

    Estimados,

    Estuve en el mismo problema hace poco, y me parece una buena solucion,

    Pero si estan con oracle 10 (creo que desde el 9 en realidad)
    hay un Pk mas util, el UTL_DBWS, es mas sencillo para los webservices,
    y el problema mas comun es solo setear el proxy, te mando una pag con la referencia, fuera de ello todo me fue de maravilla!

    Espero les sirva!

    http://www.oracle-base.com/articles/10g/utl_dbws10g.php

    y para setear el proxy, es casi igual que en el utl_http

  21. Sebas dijo:

    Marco, no me llegó en el comentario tu XML de prueba, pero cual es el error que obtienes?
    Si queres más ayuda te escribo un mail al que dejaste registrado en tu comentario.

    Saludos!

  22. Marco dijo:

    Estimado Sebas, realmente el problema en si a mi entender no es la invocacion, sino mas bien la generación del XML de la variable reqXML. Como indique anteriormente, el WSDL que me genera mi aplicacion de .NET es bastante distinto al del ejemplo por lo que pienso que quizas lo estoy armando de manera errónea.

    El WSDL de mi aplicacion es el siguiente:

    Por lo que no se exactamente que es lo que debo tomar para generar la cadena correcta del XML. Adicionalmente, se me ocurrió probar con el texto SOAP que se especifica dentro de la pagina de prueba del Webservice cuyo texto transcribo pero tampoco me funciono con este…

    int

    Que puedo hacer???

  23. Sebas dijo:

    Marco, para armar el request te sugiero que copies el xml de request que arma algún programa que permita invocar Web Services, como por ejemplo XML Spy, una vez que tengas el request armado lo asignas a la variable reqXML.
    La página que tengas que invocar para el Web Service la pones en la linea:

    req := UTL_HTTP.begin_request(‘http://servidor/ConsultaClientes', ‘POST’);

    Cambiando http://servidor/ConsultaClientes' por la página destino que corresponda.

    Espero que te sirva,
    Saludos!

  24. Marco dijo:

    Saludos, una consulta, tengo un problema al utilizar el snippet de codigo debido a que el WSDL que me genera mi webservice es muy distinto del suyo, no se si se deba a que mi WebService esta hecho en .NET, ademas dentro de su codigo no veo que ebn ningun lado se especifique la pagina ASMX para consumir el WerService, no es esto necesario?

    Una consulta adicional, el Web Service puede estar publicado en un servidor distinto a donde se encuentra la llamada del PL/SQL?

  25. Pingback: DbRunas - Invocar Web Services desde PL/SQL en Oracle

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>