Servicios Web y Strings XML

Por: Tomas Restrepo

 

Introducci�n

Es evidente para cualquier que los Web Services se han vuelto ya algo mucho m�s com�n; son no solo una soluci�n tecnol�gicamente interesante a muchos problemas, sino que es adem�s uno de esos buzz-words que los clientes mismos solicitan con frecuencia. Este art�culo trata de tocar sobre una falacia particular del desarrollo de soluciones basadas en Web Services en la que muchas veces caemos por desconocimiento de esta tecnolog�a.

 

 

El Problema

Una frase com�n que se oye cuando se habla del desarrollo de WebServices es la solicitud com�n de"Un servicio que retorne un documento XML". Aparentemente simple; al fin y al cabo, un documento XML no es m�s que una cadena de texto con un formato particular, no?

 

Bueno, pues si.... y tambi�n no. Lo que deber�a resultar aparente (y muchas veces no lo es) es que la frase "un servicio que retorne un documento XML" es redundante (lo que los gringos llaman un Oxymoron). Olvidamos acaso que el nombre original de los Web Services era "XML Web Services"?

 

Hablar de un servicio Web que retorne un XML es redundante precisamente porque el retorno[1] de XML es impl�cito en la definici�n misma de servicio Web! Y si es as�, y la presencia de XML est� intr�nseca a los servicios Web, porque nos vemos en la penosa necesidad de hablar de cadenas de texto (strings) para retornar y obtener fragmentos arbitrarios como argumentos o retornos de las operaciones en un Servicio?

 

 

Es un Problema de Perspectiva

El mundo de los Web Services, como muchos otros mundos en el universo del desarrollo de software, se visualiza para el desarrollador desde dos grandes perspectivas: El modelo real, y el modelo de programaci�n.

 

A lo que hace referencia esta divisi�n es simple: El modelo real es como funcionan realmente las cosas, mientras que el modelo de programaci�n no es m�s que una abstracci�n creada por las herramientas y lenguajes de desarrollo. El modelo real hace referencia al tiempo de ejecuci�n, mientras que el modelo de programaci�n hace referencia al tiempo de dise�o.

 

Como observamos anteriormente, la frase "un servicio que retorne un documento XML" no tiene sentido si la miramos desde la perspectiva del modelo real; pero parece tener m�s sentido si la miramos desde el punto de vista del modelo de programaci�n.

 

La manera en que la mayor parte de la gente traducir�a esta frase en un servicio funcional ser�a, como se mencion� anteriormente, definir un servicio con una operaci�n que retorne un string que contenga el texto correspondiente a un documento XML. Consideremos esta definici�n en C#/ASP.NET:

[ WebService(Namespace="urn:schemas-winterdom-com:servicio") ]

public class Servicio : WebService

{

[ WebMethod() ]

public string ObtenerXml()

{

return "<texto>Hola Mundo</texto>";

}

 

// ...

}

 

Podemos decir que la operaci�n ObtenerXml() devuelve un documento XML? Si lo miramos desde el punto de vista del c�digo fuente (el modelo de programaci�n), parecer�a evidente que la respuesta es si. Al fin y al cabo, retorna un string que contiene el texto "<texto>Hola Mundo</texto>", el cual evidentemente es un fragmento XML bien formado.

 

Consideremos ahora una segunda operaci�n:

 

 

[ WebMethod() ]

public Cliente ObtenerCliente(int id)

{

return new Cliente(id);

}

 

Podemos decir que la operaci�n ObtenerCliente() devuelve un documento XML? Bajo la vista �nicamente del modelo de programaci�n, la respuesta obvia parecer�a ser que no. Parece ser evidente que el servicio retorna un objeto de tipo Cliente, no un documento XML!

 

 

La Perspectiva Real

Retomemos ahora el servicio anterior desde la perspectiva del modelo real. Bajo la perspectiva real, sabemos, al menos en forma te�rica, sino pr�ctica, que los servicios Web son basados en SOAP, y que SOAP es en si mismo un documento XML. Veamos como se ver�a un mensaje SOAP de respuesta de cada una de estos dos operaciones.

 

Examinemos primero el mensaje SOAP de respuesta de la operaci�n ObtenerXml():

 

<?xmlversion="1.0"encoding="utf-8"?>

<soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

< soap:Body >

< ObtenerXmlResponse xmlns ="urn:schemas-winterdom-com:servicio">

<ObtenerXmlResult>&lt;texto&gt;Hola Mundo&lt;/texto&gt;</ObtenerXmlResult>

</ ObtenerXmlResponse >

</ soap:Body >

</soap:Envelope>

 

 

Si miramos el contenido del <soap:Body/> no encontramos el esperado "<texto>Hola Mundo</texto>", sino "&lt;texto&gt;Hola Mundo&lt;/texto&gt;". Humm.... ciertamente eso no parece como un fragmento bien formado de XML....

 

Miremos ahora el mensaje SOAP de respuesta para la operaci�n de ObtenerCliente():

 

<?xmlversion="1.0"encoding="utf-8"?>

<soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body>

< ObtenerClienteResponse xmlns ="urn:schemas-winterdom-com:servicio">

<ObtenerClienteResult>

<ID>12</ID>

<Nombre>John</Nombre>

<Apellidos>Doe</Apellidos>

</ ObtenerClienteResult >

</ ObtenerClienteResponse >

</ soap:Body >

</soap:Envelope>

 

 

Sorpresa! Si miramos el contenido del <soap:Body/>, podemos observar �nada m�s ni nada menos que un documento XML bien formado! No es un documento con una estructura muy definida, ni la m�s apropiada, pero sigue siendo un documento XML. (Victoria p�rrica, pero victoria al fin y al cabo).

 

 

Qu� es lo que sucede? Lo que estamos viendo es el efecto del modelo de programaci�n: No es que los Web Services retornen objetos o clases (como parecer�a ser el caso de ObtenerCliente()), sino que el modelo de programaci�n (en este caso el de ASP.NET) representa el XML y sus esquemas como objetos en el entorno de desarrollo. La mensajer�a sigue siendo XML.

 

Cuando decimos que el Servicio retorna un string (como en el caso de ObtenerXml()), pues nuevamente est� presente el modelo de programaci�n, que presume que simplemente se quiere retornar una cadena de texto cualquiera. Es decir, el modelo de programaci�n no tiene forma de saber telep�ticamente que el desarrollador esperaba enviar solo cadenas que representaran fragmentos bien formados de XML; por lo tanto, el entorno de ejecuci�n escapa la cadena para asegurar que pueda ser almacenada dentro del verdadero documento XML formado por el SOAP Envelope.

 

La Perspectiva del Contrato

Miremos ahora las diferencias en estas dos operaciones desde la perspectiva del contrato del servicio (es decir, el WSDL). No examinaremos el WSDL completo ya que ser�a demasiado largo, pero podemos concentrarnos en los elementos m�s relevantes. Examinemos en primer lugar la definici�n del portType del servicio:

 

<wsdl:portTypename="ServicioSoap">

< wsdl:operation name ="ObtenerXml">

< wsdl:input message ="tns:ObtenerXmlSoapIn" />

< wsdl:output message ="tns:ObtenerXmlSoapOut" />

</ wsdl:operation >

< wsdl:operation name ="ObtenerCliente">

< wsdl:input message ="tns:ObtenerClienteSoapIn" />

< wsdl:output message ="tns:ObtenerClienteSoapOut" />

</wsdl:operation>

</wsdl:portType>

 

Hasta aqu�, todo es igual: Tenemos el servicio con dos operaciones, cada una con un mensaje de entrada y de salida. Examinemos los mensajes de salida de ambas operaciones:

 

< wsdl:message name ="ObtenerXmlSoapOut">

< wsdl:part name ="parameters" element ="tns:ObtenerXmlResponse" />

</ wsdl:message >

< wsdl:message name ="ObtenerClienteSoapOut">

< wsdl:part name ="parameters" element ="tns:ObtenerClienteResponse" />

</ wsdl:message >

 

 

Nuevamente, hasta aqu� igual. Miremos ahora la descripci�n en el XSD de cada uno de estos dos mensajes, empezando por la de ObtenerXml():

 

<s:elementname="ObtenerXmlResponse">

< s:complexType >

< s:sequence >

< s:element minOccurs ="0" maxOccurs ="1"

name="ObtenerXmlResult"type="s:string"/>

</ s:sequence >

</ s:complexType >

</s:element>

 

Como podemos observar, la definici�n del mensaje de respuesta de la operaci�n ObtenerXml() es un elemento "ObtenerXmlResult" que contiene un string. No m�s y no menos. Por lo tanto, es claro que el WSDL no revela la estructura interna (el schema) del XML contenido en dicho string.

 

Por el contrario, la definici�n correspondiente a ObtenerCliente() es mucho m�s rica:

 

<s:elementname="ObtenerClienteResponse">

< s:complexType >

< s:sequence >

< s:element minOccurs ="0" maxOccurs ="1"

name="ObtenerClienteResult"type="tns:Cliente"/>

</ s:sequence >

</ s:complexType >

</s:element>

<s:complexTypename="Cliente">

< s:sequence >

< s:element minOccurs ="1" maxOccurs ="1" name ="ID" type ="s:int" />

< s:element minOccurs ="0" maxOccurs ="1" name ="Nombre" type ="s:string" />

< s:element minOccurs ="0" maxOccurs ="1" name ="Apellidos" type ="s:string" />

</s:sequence>

</s:complexType>

 

Como podemos ver, esta descripci�n claramente especifica que la operaci�n retorna un fragmento XML que contiene un elemento llamado ObtenerClienteResult que contiene tres sub-elementos correspondientes al ID, Nombre y Apellidos. Mucho mejor que ObtenerXml(), no? Cual de las dos operaciones creen que ser�a m�s f�cil de consumir?

 

Servicios que retornan XML

Para este punto deber�a resultar entonces evidente que si NO se debe crear servicios Web que retornen fragmentos XML como operaciones que retornen cadenas de texto. Las desventajas son claras:

 

Sin embargo, hay algunos casos en los que realmente no es posible crear servicios cuya estructura sea realmente definida previamente; es decir, algunos en los que la informaci�n de entrada o de salida pueden ser documentos XML arbitrarios, y no documentos que se ajusten a una estructura fija.

 

En estos casos, lo que se requiere es poder crear un servicio cuyas operaciones manejen fragmentos bien formados de XML arbitrarios, pero esto no quiere decir que la manera de hacerlo sea manejando strings que los contengan. WSDL, y en particular XSD, ya tienen una forma espec�fica de denotar esto en la definici�n de la estructura un mensaje mediante el uso de <xs:any/>[2] .

 

A nivel del modelo real, es decir, de la representaci�n final de dicha informaci�n, un documento arbitrario XML se representa tal como es, es decir, como XML, sin necesidad de escaparlo. A nivel del modelo de programaci�n, ASP.NET expone esta funcionalidad mediante el uso de argumentos y valores de retorno de tipo XmlNode/XmlElement/XmlDocument.

 

En realidad, esta funcionalidad es soportada directamente por el XmlSerializer, no por ASP.NET. Por lo tanto, se puede usar a diferentes niveles, como por ejemplo dentro de otro XML cuyo esquema si sea conocido.

 

Un ejemplo de esta caracter�stica ser�a la siguiente operaci�n:

 

[ WebMethod() ]

[ SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare) ]

[ return: XmlElement("Documento") ]

public XmlElement ObtenerFragmentoXml()

{

XmlDocument doc = new XmlDocument();

doc.LoadXml("<texto>Hola Mundo</texto>");

return doc.DocumentElement;

}

 

Si observ�ramos la descripci�n generada en el WSDL para esta operaci�n, ver�amos que tendr�a la siguiente forma:

 

<!-- La Operacion en el portType-->

<wsdl:operationname="ObtenerFragmentoXml">

< wsdl:input message ="tns:ObtenerFragmentoXmlSoapIn" />

< wsdl:output message ="tns:ObtenerFragmentoXmlSoapOut" />

</wsdl:operation>

 

<!-- Los Mensajes -->

<wsdl:messagename="ObtenerFragmentoXmlSoapIn"/>

<wsdl:messagename="ObtenerFragmentoXmlSoapOut">

< wsdl:part name ="ObtenerFragmentoXmlResult"

element="tns:ObtenerFragmentoXmlResult"/>

</wsdl:message>

 

<!-- Los Tipos -->

<s:elementname="Documento">

<s:complexType>

< s:sequence >

< s:any />

</ s:sequence >

</ s:complexType >

</s:element>

 

Como podemos observar, en el �ltimo fragmento de la descripci�n aparece el <xs:any/> indicando que el contenido de esa parte del mensaje es un fragmento arbitrario de XML anidado dentro de un elemento llamado "Documento". Miremos como se ver�a el mensaje de respuesta en la red:

<?xmlversion="1.0"encoding="utf-8"?>

<soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body>

< Documento xmlns ="urn:schemas-winterdom-com:servicio">

<textoxmlns="">Hola Mundo</texto>

</Documento>

</ soap:Body >

</soap:Envelope>

 

En el mensaje de respuesta se puede observar claramente como el XML retornado es tratado como XML realmente (incluyendo el manejo correcto de namespaces) y forma parte integral del mensaje, pero permitiendo que tenga una estructura arbitraria.

 

 

Contract-First

La perspectiva de Contract-First Development busca es que los Web Services se definan sea partiendo del contrato, es decir, del WSDL que los define y la mensajer�a XML que maneja el servicio, y no desde el c�digo (Code-First Development). Por analog�a, Contract-First es equivalente de cierta manera a aproximarse al dise�o del servicio desde la perspectiva del modelo real, y no como Code-First que la aproxima desde el modelo de programaci�n (como hace ASP.NET y VS.NET por defecto).

 

El tema completo de Contract-First es demasiado extenso para tratarlo como parte de este art�culo, pero es importante mencionarlo ya que problemas como los aqu� presentados se tornan evidentes desde el principio cuando se trabaja por Contract-First.

 

Estos son algunos recursos que pueden resultar �tiles para investigar sobre este tema:

 

http://www.thinktecture.com/Resources/Software/WSContractFirst/default.html

http://www.iona.com/blogs/newcomer/archives/000087.html

http://pluralsight.com/blogs/aaron/archive/2004/11/11/3440.aspx

http://msdn.microsoft.com/vstudio/java/interop/websphereinterop/default.aspx

 

 

Conclusi�n

El objetivo de este art�culo era presentar porque no se debe hacer uso de servicios Web que utilicen strings para el transporte de fragmentos de XML, bien sea como datos de entrada o de salida, dado que existen mejores (y m�s apropiadas) opciones.

 

Adicionalmente, se busc� dar claridad sobre como los modelos de programaci�n que presentan las herramientas de desarrollo en la plataforma .NET (aunque es similar en otras plataformas) pueden no siempre guiarnos hacia la mejor soluci�n; hay que conocer como funcionan los modelos realmente para poder aproximar el problema desde el mejor �ngulo.

 



[1] Lo mismo vale para la informaci�n de entrada (solicitud) del Web Service, pero se menciona solo el retorno para mantener el texto m�s simple.

 

[2] La definici�n completa del uso de <xs:any/> puede encontrarse en la secci�n 3.10 "Wildcards" de la parte 1 de la especificaci�n de XSD (http://www.w3c.org/TR/xmlschema-1/#Wildcards)