sábado, 22 de marzo de 2014

WSO2 ESB: Transformando de JSON a SOAP y de SOAP a JSON.



En esta entrada quiero mostrarles una de las maneras de implementar el siguiente escenario:

Se tiene un servicio backend implementado usando SOAP que recibe un valor de temperatura en Celsius y la devuelve en Fahrenheit, como puede ser el siguiente: http://www.w3schools.com/webservices/tempconvert.asmx?wsdl del cual nos interesa la operación CelsiusToFahrenheit.

Se desea que la petición del cliente sea un json y la respuesta igual sea un json.

La solución usando el WSO2 ESB es implementar un servicio proxy que reciba un json, lo transforme a un mensaje SOAP y lo mande al servicio backend, luego la respuesta SOAP la transforme nuevamente en un json y la mande de vuelta al cliente.

El mediador que usaremos para poder cambiar de un formato de mensaje a otro será el payloadfactory y verán lo útil que es a partir de las últimas versiones del WSO2 ESB, ya que nos permite tanto generar mensajes SOAP como JSON con bastante facilidad.

Para este servicio backend y usando el mediador PayloadFactory el servicio proxy queda como sigue:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="JsonToXMLProxy"
       transports="https http"
       startOnLoad="true"
       trace="disable">
   <description/>
   <target>
      <endpoint>
         <address uri="http://www.w3schools.com/webservices/tempconvert.asmx" format="soap11"/>
      </endpoint>
      <inSequence>
         <log>
            <property name="TEMPERATURA_ENTRADA" expression="json-eval($.celsius)"/>
         </log>
         <payloadFactory media-type="xml">
            <format>
               <web:CelsiusToFahrenheit xmlns:web="http://www.w3schools.com/webservices/">
                  <web:Celsius>$1</web:Celsius>
               </web:CelsiusToFahrenheit>
            </format>
            <args>
               <arg evaluator="json" expression="$.celsius"/>
            </args>
         </payloadFactory>
         <header name="Action"
                 value="http://www.w3schools.com/webservices/CelsiusToFahrenheit"/>
      </inSequence>
      <outSequence>
         <log>
            <property xmlns:p="http://www.w3schools.com/webservices/"
                      name="TEMPERATURA_SALIDA"
                      expression="//p:CelsiusToFahrenheitResponse/p:CelsiusToFahrenheitResult"/>
         </log>
         <payloadFactory media-type="json">
            <format>
                            "Temperatura" : {
                                "EnFahrenheit" : $1
                            }
            </format>
            <args>
               <arg xmlns:p="http://www.w3schools.com/webservices/"
                    evaluator="xml"
                    expression="//p:CelsiusToFahrenheitResponse/p:CelsiusToFahrenheitResult"/>
            </args>
         </payloadFactory>
         <property name="messageType" value="application/json" scope="axis2"/>
         <send/>
      </outSequence>
   </target>
</proxy>

Para probar el servicio usamos el siguiente comando curl:

curl -X POST -H "Content-Type: application/json" -d "{ \"celsius\": \"20\" }" "http://localhost:8280/services/JsonToXMLProxy"

Y la respuesta es la siguiente:

"Temperatura" : {
                   "EnFahrenheit" : 68
                }


Al ESB llega un mensaje como el siguiente:

POST /services/JsonToXMLProxy HTTP/1.1
User-Agent: curl/7.25.0 (i386-pc-win32) libcurl/7.25.0 OpenSSL/0.9.8u zlib/1.2.6 libssh2/1.4.0
Host: 127.0.0.1:4444
Accept: */*
Content-Type: application/json
Content-Length: 19

{ "celsius": "20" }

Este mensaje json primero es capturado por un log que imprime en consola el valor de celsius usando la siguiente expresión: json-eval($.celsius)

2014-03-21 22:44:22,976] INFO - LogMediator To: /services/JsonToXMLProxy, MessageID: urn:uuid:60f0298e-f66d-4f9c-a2a9-5480ee2569a5, Direction: request, TEMPERATURA_ENTRADA = 20

Luego usando el mediador payloadfactory se crea un mensaje de acuerdo a la estructura que espera el servicio backend. Para saber su estructura siempre me apoyo en la herramienta SOAPUI, probando primero el servicio. El valor que se le pasa en el atributo se obtiene a partir de evaluar como json la expresión $.celsius.

Algo importante a tener en cuenta es especificar el Header, lo cual se hace usando el mediador header.
Y así de esta manera se termina la secuencia de entrada.

El mensaje creado es enviado al endpoint definido y su respuesta que viene en este formato, debe ser procesada:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.w3schools.com/webservices/">
   <soapenv:Body>
      <web:CelsiusToFahrenheitResponse>
         <!--Optional:-->
         <web:CelsiusToFahrenheitResult>68</web:CelsiusToFahrenheitResult>
      </web:CelsiusToFahrenheitResponse>
   </soapenv:Body>
</soapenv:Envelope>

Cuando este mensaje llega al ESB lo primero que hacemos es obtener en un log el valor de la temperatura, y se imprime como sigue en pantalla:

[2014-03-21 22:44:23,109] INFO - LogMediator To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: , SOAPAction: , MessageID: urn:uuid:c76239bb-16cf-459d-99e5-5296d5aba938, Direction: response, TEMPERATURA_SALIDA = 68

Luego para crear la respuesta que enviaremos al cliente nos apoyamos en otro mediador payloadfactory en la secuencia de salida, definiendo que el tipo será json y especificando la estructura del mensaje de salida. El valor del argumento se obtiene a partir de conocer la estructura del mensaje SOAP que llega al ESB desde el servicio backend. Importante no dejar de incluir la propiedad de nombre messageType.

Y eso es todo, hay otras maneras de implementar este escenario bien sea usando el mediador script o el XSLT, pero en este caso me gusta más usar el payloadfactory.

Espero les sea de utilidad.

1 comentario:

  1. Muy interesante y claro el uso de payloadFactory.
    Gracias

    ResponderEliminar