WSS 3.0 & MOSS: Atacando los servicios web !

Estos días en el CIIN estamos preparando un curso de desarrollo en WSS 3.0 (Ángel y yo), y uno de los puntos que se tratará (de los muchos que hemos incluido, porque se pueden hacer muchas cosas como podéis comprobar en el Microsoft Office Interactive Developer Map) es como acceder a dato de una lista de WSS 3.0 de manera remota a través de la llamada a los servicios web de WSS 3.0 y como los datos que leamos los podemos volcar de manera remota en otra lista.

Recopilatorio_WSS_Y_MOSS_VIII_3


Este post extiende a otro previo en el que explicábamos como copiar datos entre listas de WSS 3.0 utilizando el modelo de objetos (OM) de WSS 3.0. Ahora bien, este ejemplo es válido siempre que las listas estén en el mismo servidor de WSS 3.0, es decir, en el mismo contexto. Sin embargo, el OM no es aplicable en el caso en el que queramos acceder de manera remota a datos de un servidor WSS 3.0. Para este escenario, tenemos dos posibilidades:


  • Atacando los servicios web que expone WSS 3.0.
  • Utilizando los dos protocolos RPC que tenemos para comunicaciones remotas en WSS 3.0 sin utilizar los servicios web que expone:

    • FrontPage Server Extensions Remote Procedure Call (FrontPage RPC).
    • Windows Sharepoint Services Remote Procedure Call (WSS 3.0 RPC).

En este post (y cuya redacción, código, etc hemos realizado conjuntamente Ángel y yo, de hecho Ángel se ha “pegado” mucho con los servicios web de WSS 3.0 & MOSS y ha sido quien me ha enseñado y explicado todos los aspectos a tener en cuenta, como funciona el proceso de lectura / escritura de datos en una lista de WSS 3.0, etc.) cubriremos el primer punto. El segundo punto aún no lo hemos probado, pero para los intrépidos deciros que el capítulo 9 de Developer’s Guide To Windows Sharepoint Services 3.0 de Tood C. Bleeker cubre este tópico.

En el ejemplo desarrollado, leeremos los datos de una lista de tipo calendario ubicada en un cierto site collection y los volcaremos en una lista del mismo tipo, pero ubicada en otro site collection distinto. Por lo tanto, en este sencillo caso sólo tendremos que añadir una referencia web al servicio web de WSS 3.0 http://[NommbreServidor]/_vti_bin/Lists.asmx pues vamos a mover datos entre listas que están en el servidor. Para el caso de trabajar con listas ubicadas en site collections pertenecientes a distintos servidores web habría que añadir una segunda referencia al servicio web donde reside la lista destino (si bien, y como veremos en el código utilizado para demostrar como operar con los servicios web, no es necesaria una segunda referencia): http://[NommbreServidor2]/_vti_bin/Lists.asmx. Empecemos.

Paso 1: Añadir la referencia web del servidor Origen

Lo primero que tenemos que hacer una vez creado nuestro proyecto de prueba (por simplicidad, hemos elegido uno de aplicación de consola) es añadir la referencia web al servicio web de WSS 3.0 que vamos a utilizar y que en este caso (para trabajar con listas) es http://[NommbreServidor]/_vti_bin/Lists.asmx:

Post_Atacando_ServiciosWeb_Add_WebReference

Una vez añadida la referencia web, en el archivo Program.cs añadimos las referencias necesarias para interactuar con servicios web y para trabajar con XML.

Using System.Net;

Using System.Xml;

Using Syste.Xml.XPath;

Paso 2: Lectura de los datos de una lista de WSS 3.0

Lo primero que tenemos que hacer es crear una instancia del objeto Lists() asociado al servicio web. Una vez hecho esto, le especificamos los siguientes miembros:



  • Las credenciales de acceso que especificamos a través de un objeto de tipo NerworkCredentials. El usuario y contraseña son los mismos con los que accedemos a la máquina virtual (Administrator, pass@word1).
  • La url del servicio web Lists.asmx para la lista origen: http://litwaredemo/sites/Desarrollo/_vti_bin/Lists.asmx.

LitwareDemo_WS.Lists servWeb = new LitwareDemo_WS.Lists();

//Credenciales para el acceso al servicio web

servWeb.Credentials = new NetworkCredential(“Administrator”, “pass@word1”);

servWeb.Url=http://litwaredemo/sites/Desarrollo/_vti_bin/Lists.asmx;

De esta forma ya podemos acceder a listas del site collection origen. Para acceder a los datos de la lista calendario tenemos que seguir los siguientes pasos:


  • Creamos un objeto de tipo XmlNode a partir del método GetListItems del objeto Lists() creado. De todos los parámetros que admite el método, basta con que especifiquemos el nombre de la lista (Nota: Hay que tener en cuenta que esto es así por la sencillez del escenario tratado).
  • Para ver que estamos accediendo correctamente a los datos de la lista, mostramos por pantalla la estructura interna XML a partir del miembro InnerXML (también podríamos visualizar la estructura XML con OuterXML).

XmlNode xmlNodo = servWeb.GetListItems(“Calendario”, null, null, null, null, null, null);

Console.WriteLine(xmlNodo.InnerXml);

Console.ReadLine();

Si probamos este código, la salida por pantalla que obtendremos debería ser la siguiente (en este caso hay tres eventos en el calendario):

Post_Atacando_ServiciosWeb_EstructuraXMLRecibida


Lo cual concuerda con los datos que tenemos en la lista Calendario del site collection origen:

Post_Atacando_ServiciosWeb_CalendarioOrigen


Paso 3: Escritura de los datos en la lista destino

En esta sección veremos cómo copiar los datos de la lista Calendario de http://litwaredemo/sites/Desarrollo a la lista Calendar de http://litware. Los pasos que seguiremos son los siguientes:


  • A partir de los datos leídos de la lista origen mediante GetListItems, crearemos la estructura XML necesaria para poder llevarlos a la lista destino, es decir, no es suficiente con pasar la estructura XML que estamos leyendo sino que tenemos que realizar las transformaciones necesarias para construir un documento XML que sea entendible en destino.
  • Mediante el método UpdateListItems (también del objeto Lists()), realizaremos la copia efectiva de los datos en la lista destino a partir del documento generado en el formato que espera este método. En este enlace de msdn podéis ver ejemplos de cómo tiene que ser el documento XML a construir para utilizarlo con UpdateListItems.

Para realizar los pasos anteriores, es necesario realizar los siguientes puntos:

i) Crear un objeto de tipo XDocument, y que va a representar un documento XML completo

ii) Crear un objeto de tipo XmlNamespaceManager a partir de la propiedad NameTable del objeto XDocument creado en el paso anterior. Este objeto nos permite añadir al documento a crear los espacios de nombres necesarios (y que identificamos a partir de la estructura Xml devuelta en el paso 1).

iii) Añadimos al XmlNameSpaceManager los siguientes espacios de nombres

a. Prefijo rs, nombre del esquema urn:schemas-microsoft-com:rowset. Este espacio de nombres se corresponde con el elemento rs:Data que identifica el número de elementos que constituyen el archivo Xml origen (devuelto en la llamada al método GetListItems()).

b. Prefijo z, nombre del esquema #RowsetSchema. Este espacio de nombres identifica un elemento concreto dentro del documento a generar.

XmlDocument xmlDoc = new XmlDocument();

XmlNamespaceManager xnsmManager = new XmlNamespaceManager(xmlDoc.NameTable);

xnsmManager.AddNamespace(“rs”,”urn:schemas-microsoft-com:rowset”);

xnsmManager.AddNamespace(“z”, “#RowsetSchema”);

iv) Crear un objeto de tipo XPathNavigator a partir del objeto XmlNode y su método CreateNavigator() para explorar el documento (XPathNavigator permite el desplazamiento por los nodos de atributo y por los nodos de espacio de nombres de un documento XML) obtenido en la sección anterior.

v) Crear un objeto de tipo XPathNodeIterator que nos proporciona un iterador sobre los elementos del objeto XPathNavigator utilizando el método Select del mismo. Comoo parámetros del método especificaremos:

a. La expresión XPath de búsqueda que es //z:row.

b. El objeto XmlNamespaceManager que contiene los espacios de nombres añadidos y que se utilizan en la correspondiente resolución de nombres.

vi) Creamos un objeto de tipo XmlElement que será el elemento primario del árbol XML a contruir y cuyo nombre ha de ser Batch. A este objeto le añadimos el atributo OnError con valor Continue.

XPathNavigator xpnNavegador = xmlNodo.CreateNavigator();

XPathNodeIterator xpniIterator = xpnNavegador.Select(“//z:row”, xnsmManager);

XmlElement xmlBatchElement = xmlDoc.CreateElement(“Batch”);

xmlBatchElement.SetAttribute(“OnError”, “Continue”);

Nota: En este enlace podéis ver cómo ha de ser la estructura del documento XML que hay que pasar al servicio Lists.asmx para escribir nuevos datos en una lista de WSS 3.0 o actualizar datos existentes.

vii) A continuación, nos tenemos que mover en modo iterativo (pues tenemos más de un elemento z: en la estructura XML devuelta en la llamada) por el objeto XPathNodeIterator utilizando para ello el método MoveNext(). Lo que haremos en cada iteración es lo siguiente:


a. Definir un objeto XmlElement con nombre Method. Le añadiremos dos atributos: ID con el valor de la iteración correspondiente (empezamos en 0) y que identifica el elemento a añadir (en el caso de actualización, identifica el elemento que se va actualizar y que no tiene porque corresponderse con el ID de lista del elemento que se está actualizando), y Cmd que significa que estamos creando un nuevo elemento en el árbol (de ahí que su valor sea New, en el caso de actualización sería update). A continuación añadimos el elemento creado al elemento Batch utilizando el método AppendChild al que le pasamos él elemento a añadir.


XmlElement methodElement = xmlDoc.CreateElement(“Method”);

methodElement.SetAttribute(“ID”, i.ToString());

methodElement.SetAttribute(“Cmd”, “New”);

xmlBatchElement.AppendChild(methodElement);

Siguiendo la estructura del documento XML a generar que tenemos que pasarle a Lists.asmx, vemos que un elemento de tipo Method está compuesto de varios elementos de tipo Field, identificando el primero de ellos que vamos a hacer: crear un nuevo elemento (en este caso tendrá el atributo Name con valor New) o actualizar uno existente (en cuyo caso e atributo Name tendrá como valor el ID del elemento a actualizar). Por así decirlo, el primer elemento de tipo Field nos da la clave primaria del elemento correspondiente en la lista destino de WSS 3.0. Así, estos serían dos ejemplos de los documentos XML a generar para cada caso:

XML a generar para actualizar elementos de la lista destino

Post_Atacando_ServiciosWeb_EstructuraXMLUpdate

Como veis, en este caso el elemento Method tiene Cmd con valor Update, el ID vale 1 lo que indica que estamos trabajando con el primer elemento de la estructura a actualizar. A continuación vienen los elementos Field. El primero indica que vamos a actualizar el elemento de la lista destino con ID 4, y el segundo que actualizaremos el campo Field_Name con el valor Value.

XML a generar para crear nuevos elementos en la lista destino

Post_Atacando_ServiciosWeb_EstructuraXMLNew

En este caso la idea es la misma, salvo que ahora en Method el atributo Cmd tiene un valor New, y que el primer elemento Field tiene un valor New para el atributo ID.

En los siguientes puntos veremos cómo ir añadiendo al elemento Method los elementos Field necesarios, empezando por el primero que nos indica que se está añadiendo un nuevo elemento en la lista destino.


b. Definimos un objeto XElement con nombre Field. Este primer elemento Field tendrá un atributo Name con valor ID y propiedad InnterText con valor New para especificar que se va a añadir un nuevo elemento al árbol (como nodo hijo del elemento Method mediante el método AppendChild())


XmlElement fieldElement0 = xmlDoc.CreateElement(“Field”);

fieldElement0.SetAttribute(“Name”, “ID”);

fieldElement0.InnerText = “New”;

methodElement.AppendChild(fieldElement0);


c. Definir un objeto XmlElement por cada uno de los campos de la lista origen que queremos copiar / crear en la lista destino. Estos elementos los añadiremos como nodos hijos al elemento Method. El primer elemento que añadiremos es el título del evento, por lo que tendremos que añadir al elemento el atribute Title. El valor de la propiedad InnerText se obtiene en este caso de buscar en el ítem actual del iterador el atributo equivalente: ows_Title. Este valor lo obtenemos a través del método GetAttribute del elemento actual del objeto XPathNodeIterator al que le especificamos el string a buscar (el nombre del atributo) y el esquema en el que se encuentra:


XmlElement fieldElement1 = xmlDoc.CreateElement(“Field”);

fieldElement1.SetAttribute(“Name”, “Title”);

bool bAttrib = xpniIterator.Current.HasAttributes;

fieldElement1.InnerText = xpniIterator.Current.GetAttribute(“ows_Title”,

xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement1);


d. Lo que hemos hecho para el título del evento lo repetimos para los siguientes elementos:

Post_Atacando_ServiciosWeb_TablaDatos


El código completo de construcción del documento Xml necesario para copiar los datos indicados en la tabla en la lista destino es:

while (xpniIterator.MoveNext())

{

//Method element

XmlElement methodElement = xmlDoc.CreateElement(“Method”);

methodElement.SetAttribute(“ID”, i.ToString());

methodElement.SetAttribute(“Cmd”, “New”);

xmlBatchElement.AppendChild(methodElement);

//Field Element que especifica que se va a añadir un nuevo ítem

XmlElement fieldElement0 = xmlDoc.CreateElement(“Field”);

fieldElement0.SetAttribute(“Name”, “ID”);

fieldElement0.InnerText = “New”;

methodElement.AppendChild(fieldElement0);

//********************************************************************

//Field elements a actualizar

//********************************************************************

XmlElement fieldElement1 = xmlDoc.CreateElement(“Field”);

fieldElement1.SetAttribute(“Name”, “Title”);

bool bAttrib = xpniIterator.Current.HasAttributes;

fieldElement1.InnerText = xpniIterator.Current.GetAttribute(“ows_Title”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement1);

//Título

XmlElement fieldElement2 = xmlDoc.CreateElement(“Field”);

fieldElement2.SetAttribute(“Name”, “Location”);

fieldElement2.InnerText = xpniIterator.Current.GetAttribute(“ows_Location”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement2);

//Fecha de Inicio

XmlElement fieldElement3 = xmlDoc.CreateElement(“Field”);

fieldElement3.SetAttribute(“Name”, “EventDate”);

fieldElement3.InnerText = xpniIterator.Current.GetAttribute(“ows_EventDate”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement3);

//Fecha de Fin

XmlElement fieldElement4 = xmlDoc.CreateElement(“Field”);

fieldElement4.SetAttribute(“Name”, “EndDate”);

fieldElement4.InnerText = xpniIterator.Current.GetAttribute(“ows_EndDate”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement4);

//Descipción

XmlElement fieldElement5 = xmlDoc.CreateElement(“Field”);

fieldElement5.SetAttribute(“Name”, “Description”);

fieldElement5.InnerText = xpniIterator.Current.GetAttribute(“ows_Description”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement5);

//All Day Event

XmlElement fieldElement6 = xmlDoc.CreateElement(“Field”);

fieldElement6.SetAttribute(“Name”, “fAllDayEvent”);

fieldElement6.InnerText = xpniIterator.Current.GetAttribute(“ows_fAllDayEvent”, xnsmManager.DefaultNamespace);

methodElement.AppendChild(fieldElement6);

i++;

}

Una vez construido el documento Xml que se va a enviar al destino, la salida por pantalla que obtenemos es la siguiente:

Post_Atacando_ServiciosWeb_EstructuraXMLGenerada

Nota: La salida se ha obtenido con Console.WriteLine(xmlBatchElement.OuterXml)


e. Una vez que hemos añadido todos los elementos para cada iteracción, ya tendremos construida la estructura Xml necesaria para copiar los datos de la lista origen a la lista destino. Para ello, especificamos la url del servicio web Lists.asmx para la lista destino, indicamos las credenciales (que son las mismas en este caso) y llamamos al método UpdateListItems del objeto Lists() y al que le pasamos el nombre de la lista en la que vamos a añadir los datos y la estructura XML que hemos construido y que contiene los datos a crear:


//Creamos los elementos en el destino

servWeb.Url = “http://litwaredemo/_vti_bin/Lists.asmx”;

servWeb.Credentials = new NetworkCredential(“Administrator”, “pass@word1”);

try

{

servWeb.UpdateListItems(“Calendar”, xmlBatchElement);

Console.WriteLine(“Datos copiados en el destino”);

}

catch (Exception ex)

{

Console.WriteLine(ex.Message);

}

Console.ReadLine();

Sin más, comprobamos que la lista Calendar de http://litwaredemo tiene los eventos añadidos.

Post_Atacando_ServiciosWeb_CalendarioDestino


Lo mismo que hemos hecho con dos listas de sites collections diferentes, pero ubicados en el mismo servidor de WSS 3.0 se puede hacer para el caso en el que estén en servidores distintos. Lo único que habría que hacer es añadir una segunda referencia web al proyecto y utilizar dicha referencia web al copiar los elementos en la lista destino.


Y esto es lo que os queríamos contar respecto al trabajo con listas de WSS 3.0 a través de los servicios web que exponen. Esperamos que el post os haya resultado interesante y le encontréis utilidad. Os dejamos aquí el código completo del ejemplo desarrollado

Ángel y JC

Publicado por

Juan Carlos González

Juan Carlos es Ingeniero de Telecomunicaciones por la Universidad de Valladolid y Diplomado en Ciencias Empresariales por la Universidad Oberta de Catalunya (UOC). Cuenta con más de 12 años de experiencia en tecnologías y plataformas de Microsoft diversas (SQL Server, Visual Studio, .NET Framework, etc.), aunque su trabajo diario gira en torno a SharePoint & Office 365. Juan Carlos es MVP de Office Servers & Services desde 2015 (anteriormente fue reconocido por Microsoft como MVP de Office 365 y MVP de SharePoint Server desde 2008 hasta 2015), coordinador del grupo de usuarios .NET de Cantabria (Nuberos.Net, www.nuberos.es), co-fundador y coordinador del Grupo de Usuarios de SharePoint de España (SUGES, www.suges.es), así como co-director de la revista gratuita en castellano sobre SharePoint CompartiMOSS (www.compartimoss.com). Hasta la fecha, ha publicado 8 libros sobre SharePoint & Office 365 y varios artículos en castellano y en inglés sobre ambas plataformas.

Un comentario en “WSS 3.0 & MOSS: Atacando los servicios web !”

Deja un comentario

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