Manipular el Header en un servicio REST en Oracle Service Bus 12.2.1
Por Sandra Flores
Publicado en Junio 2016
En los últimos años los servicios de tipo REST han cobrado alta relevancia en aplicaciones para dispositivos móviles e incluso para aplicaciones Web que desean mantener el intercambio de información de forma ligera. En soporte a esto, la versión 12c de SOA ha incluido facilidades para usar REST de manera más sencilla. Una de las complejidades que puede tener este tipo de servicios es el manejo del Header, mismo que a diferencia de los servicios SOAP, debe viajar en el header del transporte como atributos de header de usuario.
En este artículo hablaremos de cómo exponer en OSB 12.2.1 un servicio proxy de tipo REST a partir de uno de tipo SOAP, mismo que fue creado a partir de sus respectivos archivos WSDL y XSD, pero con un factor de complejidad aumentado: El escenario plantea un servicio SOAP que requiere el uso de atributos en el elemento header del SOAP Envelope, por ende, cuando es publicado como REST, debe soportar la misma característica de alguna forma. Debido a que en este ejemplo en particular se desea tener solo un Pipeline para ambas interfaces (SOAP y REST) el header debería ser soportado en ambos casos y tratado de la misma manera, en la medida de lo posible. Para esto les comparto mi solución.
Comenzamos por la definición del WSDL y XSD del servicio SOAP llamado Persona. Es muy importante resaltar cómo están declarados los elementos involucrados en la definición del mensaje correspondiente al header.
Persona.wsdl:
PersonaSchema.xsd:
Lo más relevante en el WSDL es que los mensajes deben tener solo una parte definida, de lo contrario, cuando se requiera exponer el pipeline como REST no mostrará la opción Expose As REST, debido a que dichos servicios no soportan mensajes multipart. Por ejemplo, si quisiéramos hacer algo como esto, sería incorrecto:
El siguiente paso es crear el servicio proxy a partir del WSDL y el XSD. Para esto, en JDev una vez que se ha creado la estructura del proyecto y ambos archivos, dar clic derecho sobre Persona.wsdl, Service Bus, Generate Proxy Service.

Esto abrirá la ventana de creación donde debemos configurar el nombre del servicio proxy SOAP, seleccionamos el binding que creamos previamente el WSDL, y seleccionamos la opción Generate Pipeline. Next.

Seleccionamos el transporte y especificamos el Enpoint URI deseado. Finish.

Se crearán los archivos solicitados. Yo cambié la ubicación de los archivos hacia las carpetas correspondientes para tener un mejor control visual.

Ahora abramos el archivo llamado Persona (equivalente al Composite.xml en BPEL), localizado en la parte inferior izquierda del proyecto. Clic derecho sobre el Pipeline y Expose As REST.

En la ventana de creación estableceremos el nombre del servicio proxy de tipo REST, en este caso PersonaRest. Next.

Luego, en la ventana de configuración de recursos, establecemos las operaciones especificadas en el WSDL y las ligamos a un recurso y su correspondiente path asociado al servicio REST.

Doble clic sobre el registro vacío de la tabla Resources, mismo que tiene solo una diagonal '/'. Esto abrirá la ventana para establecer el path del recurso. En este caso yo le llamé /altaPersona. OK.

Una vez agregado el recurso, doble clic en el registro de la tabla Operation Bindings.

Esto abrirá la ventana de configuración donde elegiremos el recurso y el verbo HTTP, en este caso escogí POST debido a que mi operación ficticia es un insert en el servidor. En la pestaña de Request, se ha mapeado automáticamente el elemento del XSD, y podemos elegir si el servicio REST recibirá mensajes en formato JSON y XML, entre otras cosas.

Para ver un ejemplo del mensaje de request en el formato que deseemos, dar clic en el ícono con los engranes. Una vez revisado, OK o Cancel para cerrar la ventana.

Una vez que el request ha quedado establecido, es necesario configurar el response, para logarlo es necesario dar clic en la pestaña Response, seleccionar el formato del payload y de igual forma, se puede visualizar el ejemplo del mensaje en varios formatos.


Una vez que hemos terminado de vincular las operaciones con los recursos, veremos la pantalla completa como se muestra a continuación. Finish para terminar.

Después de finalizar la creación automática, veremos ligados al Pipeline, al servicio SOAP y al REST. Ahora editemos el Pipeline para agregar un poco de funcionalidad y así obtener los atributos del header que nos interesan. Doble clic en el Pipeline.

Agregué una actividad Operational Branch y dentro de ésta agregué un PipelinePair para la operación registrarAltaPersona y en el Response Stage puse una actividad Replace para simular la respuesta Mock del servicio y poder probarlo.

La actividad Replace se ve de esta manera:

Inicialmente escribí el texto del XML de respuesta tal como lo regresaría el servicio tipo SOAP y usé la función inLinedXML para convertirla en un XML.

Con esto podemos probar que el servicio funciona bien en ambos proxies, SOAP y REST.




Ahora bien, la parte más interesante de este post está relacionada a leer el header de los mensajes del servicio de tipo REST, por lo que modifiqué el xml de respuesta para obtener del header los dos atributos; idSistema y token, y los concatené a la salida del mensaje para que sean fácilmente visibles en la prueba, por lo que la expresión queda de la siguiente forma:

Podemos notar que los valores de los atributos del header para el servicio REST se obtienen de la variable inbound:
$inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSistema']/@value
Despleguemos y probemos el servicio enviándole los atributos idSistema y token en el header del transporte HTTP. Esto lo podemos hacer en la consola de pruebas de OSB en la sección User Headers en la parte Transport.



Tal como se puede apreciar en la imagen, la respuesta obtenida muestra los valores que enviamos en el header del transporte HTTP.
Como paso adicional, decidí asignar a la variable header los valores de nuestro user header que se obtienen de inbound. Con esto logramos que de ese momento en adelante, se pueda trabajar con una sola estructura en el pipeline del proxy, sin tener que hacer validaciones para cuando haya sido una invocación vía REST o una SOAP.
Para este fin, implementé una transformación XQuery que recibe las variables header e inbound, hace las validaciones de un caso u otro y deja los valores en header. El código fuente del archivo XQuery queda de la siguiente forma:
xquery version "1.0" encoding "utf-8";
(:: OracleAnnotationVersion "1.0" ::)
declare namespace soap="http://schemas.xmlsoap.org/soap/envelope/";
(:: import schema at "../XSD/soap-env.xsd" ::)
declare namespace ctx="http://www.bea.com/wli/sb/context";
(:: import schema at "../XSD/MessageContext.xsd" ::)
declare namespace tp="http://www.bea.com/wli/sb/transports";
declare namespace http = "http://www.bea.com/wli/sb/transports/http";
declare namespace per="http://schema.blog.com.mx/Persona";
(:: import schema at "../XSD/PersonaSchema.xsd" ::)
declare variable $requestHeader as element() (:: schema-element(soap:Header) ::) external;
declare variable $inbound as element() (:: schema-element(ctx:endpoint) ::) external;
declare function local:establecerRequestHeader($requestHeader as element() (:: schema-element(soap:Header) ::),
$inbound as element() (:: schema-element(ctx:endpoint) ::)) {
{
if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSistema']/@value))
then
{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSistema']/@value)}
else if (exists($requestHeader/per:requestHeader/per:idSistema))
then
{$requestHeader/per:requestHeader/per:idSistema/text()}
else
()
}
{
if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='token']/@value))
then
{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='token']/@value)}
else if (exists($requestHeader/per:requestHeader/per:token))
then
{$requestHeader/per:requestHeader/per:token/text()}
else()
}
{
if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idTransaccion']/@value))
then
{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idTransaccion']/@value)}
else if (exists($requestHeader/per:requestHeader/per:idTransaccion))
then
{$requestHeader/per:requestHeader/per:idTransaccion/text()}
else()
}
};
En el pipeline, agregué una actividad Replace para reemplazar la variable header con la salida del XQuery. El pipeline queda de la siguiente manera:

Las propiedades de la actividad Replace se ven así:


Por último, cambié la expresión de Replace sobre el body de respuesta para tomar un valor del header REST y uno del SOAP, solo para ilustrar que una vez que ha pasado la transformación, es viable usar la estructura header.
En este paso, las lineas quedan de la siguiente forma:
fn-bea:inlinedXML(fn:concat('
',$inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSistema']/@value,'
',$header/per:requestHeader/per:token/text(),'
'))Es probable que en este punto se presente un error al editar la función xQuery, esto debido a que no está declarado el namespace con prefijo per, si esto ocurre, solo es necesario agregarlo en la pestaña namespaces.

Para validar el resultado, despleguemos y probemos el proxy REST, enviando los headers idSistema y token

Como se muestra en la imagen, estamos obteniendo el valor de los headers ya sean provenientes de un request de tipo REST, o bien, de tipo SOAP, lo unificamos en la variable header y en adelante, es posible usarlos como si hubiesen sido enviados por SOAP.
Con esto damos por finalizado este ejemplo, espero haya sido de utilidad.
Sandra Flores es Ingeniera en sistemas computacionales con más de 8 años de experiencia laboral, egresada de la Escuela Superior de Cómputo del IPN México. He realizado gran variedad de actividades relacionadas al desarrollo de sistemas. Enfocada al desarrollo de soluciones con Oracle Fusion Middleware así como desempeñar el cargo de SOA Sr. Consultant.
Este artículo ha sido revisado por el equipo de productos Oracle y se encuentra en cumplimiento de las normas y prácticas para el uso de los productos Oracle.