Consumir SOAP desde WinJS


soap
No todos los servicios proporcionan una API Restful para acceder a ella de forma sencilla desde nuestras Apps, así que es posible que tengamos que pelearnos con servicios SOAP, que son más pesados y complicados, pero a la vez nos permiten trabajar operaciones y conjuntos de datos más complejos.
Consumir SOAP desde JavaScript no es complicado, sólo que es tedioso. Hoy vamos a ver dos maneras que podremos utilizar en nuestras aplicaciones de la tienda Windows.

Descarga el código de ejemplo de Codeplex

Consumir SOAP con xhr

WinJS.xhr es un wrapper del objeto XMLHttpRequest para facilitarnos las llamadas asíncronas, así que utilizar SOAP desde xhr va a ser tan sencillo o complicado como con el XMLHttpRequest original.

Para el siguiente código vamos a utilizar uno de los ejemplos de servicios web SOAP que hay en http://www.webservicex.net/, en concreto el servicio ChangeLengthUnit que realiza conversión de unidades.

En una aplicación de la tienda Windows creamos una página y en el tag body ponemos un cuadro de texto, un botón y un div como estos:

Introduce los centímetros: <input type="number" id="mmValue" value="0.0"/>
<button id="xhrTest">Conexión con WinJS.xhr</button>
<div id="result"></div>

En default.js, añadimos el código que sigue tras la línea donde se llama a WinJS.UI.ProcessAll(), creando el código XML que enviaremos al servicio SOAP para llamar a la acción SOAP ChangeLengthUnit. Podéis ver un ejemplo en la página de descripción de la operación, necesita tres parámetros: el valor numérico, las unidades origen y las unidades destino. Para el ejemplo convertiremos de centímetros a pulgadas:

var data = '<?xml version="1.0" encoding="utf-8"?>' +
            '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 
            ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
            ' xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
            '  <soap:Body>' +
            '    <ChangeLengthUnit xmlns="http://www.webserviceX.NET/">' +
            '      <LengthValue>'+mmValue.value+'</LengthValue>' +
            '      <fromLengthUnit>Centimeters</fromLengthUnit>' +
            '      <toLengthUnit>Inches</toLengthUnit>' +
            '    </ChangeLengthUnit>' +
            '  </soap:Body>' +
            '</soap:Envelope>';

var options = {
    url: "http://www.webservicex.net/length.asmx",
    type: "post",
    headers: {
        "Content-Type": "text/xml; charset=utf-8",
        "SOAPAction": "http://www.webserviceX.NET/ChangeLengthUnit"
    },
    data: data
};

Fijaos que he configurado la llamada xhr para llamar por POST y he incluido dos cabeceras específicas para la llamada, el Content-Type, que debe ser xml y la SOAPAction, que debe indicar a qué servicio estamos llamando. Ahora llamamos a la función xhr con las opciones que hemos configurado y esperamos la respuesta, que vuelve a ser un XML del que podremos extraer la información.

WinJS.xhr(options)
.done(
    function (request) {
        var doc = request.responseXML.documentElement;
        var output = doc.getElementsByTagName("ChangeLengthUnitResult");

        result.innerHTML = "La longitud es "+ window.toStaticHTML(output[0].textContent)+" pulgadas";
        result.style.backgroundColor = "#00A000";
    },
    function (error) {
        result.innerHTML = "Status: " + error.status + " : " + error.statusText;
        result.style.backgroundColor = "#FF0000";
    },
    function (progress) {
        result.innerText = "Ready state is " + progress.readyState;
        result.style.backgroundColor = "#0000A0";
    });

Todo esto para un servicio web bastante sencillo. Si empezamos a añadir tipos de datos complejos la cosa se complica.
Como estamos en Windows 8 / 8.1, tenemos una gran ventaja, podemos realizar la conexión con el servicio web mediante C#, que es capaz de leer la descripción WSDL del servicio y generar un proxy con las clases tipificadas, para luego utilizar esa clase desde JavaScript a través de las proyecciones WinMD.

Puente SOAP con C#

Como ya comenté en un artículo anterior, podemos utilizar librerías escritas en C# o C++ desde nuestros proyectos JavaScript, sólo tenemos que crear una librería tipo Windows Runtime Component y respetar unas normas básicas. Una vez creada la librería y referenciada desde nuestro proyecto JavaScript, utilizar servicios SOAP es muy fácil, basta con pulsar botón derecho sobre el proyecto C# y añadir una referencia al servicio. El Visual Studio nos creará las clases y llamadas que necesitamos para comunicarnos con el mismo.

Add WebService

Con la referencia al servicio ya tenemos todo lo necesario, sólo nos falta crear un método dentro de una clase C# para publicarlo y que se pueda utilizar desde JavaScript. Como las llamadas al servicio son asíncronas creamos un método que devuelve un tipo IAsyncOperation; podemos convertir la Task del cliente SOAP directamente a este interfaz con el método de extension AsAsyncOperation:

public sealed class LengthUnits
{
    LengthUnitService.lengthUnitSoapClient client;
    public LengthUnits()
    {
        client = new LengthUnitService.lengthUnitSoapClient();
    }
    public IAsyncOperation<double> GetAsync(double number,
        LengthUnitService.Lengths from, LengthUnitService.Lengths to)
    {
        return client.ChangeLengthUnitAsync(number, from, to).AsAsyncOperation();
    }
}

Et voilà, ya lo podemos utilizar desde JavaScript sin ensuciarnos las manos parseando XML:

csTest.onclick = function (e) {
    var cl = new SoapClient.LengthUnits();
    cl.getAsync(mmValue.value, SoapClient.LengthUnitService.Lengths.millimeters, SoapClient.LengthUnitService.Lengths.inches).done(
        function (c) {
            result.innerHTML = "La longitud es " + c + " pulgadas";
            result.style.backgroundColor = "#00a000";
        },
        function (err) {
            result.innerHTML = window.toStaticHTML(err);
            result.style.backgroundColor = "#FF0000";
        },
        function (progress) {
            result.innerHTML = window.toStaticHTML("percent: " + progress + "%");
            result.style.backgroundColor = "#0000a0";
        }
        )
}

Progreso

Un momento, ¿ahora no está llegando nunca a la función de progreso? Pues no, porque para tener progreso necesitamos utilizar el interfaz IAsyncOperationWithProgress. Nos llevará un poco más de trabajo, la clase AsyncInfo y las palabras async y await nos lo van a poner fácil:

    public Windows.Foundation.IAsyncOperationWithProgress<double, string> GetWithProgressAsync(double number,
        ServiceReference1.Lengths from, ServiceReference1.Lengths to)
    {
        return AsyncInfo.Run(async delegate(CancellationToken cancellationToken,
            IProgress<string> progress)
        {
            double result;
            progress.Report(client.State.ToString());
            await client.OpenAsync();
            progress.Report(client.State.ToString());
            result = await client.ChangeLengthUnitAsync(number, from, to);
            progress.Report(client.State.ToString());
            await client.CloseAsync();
            progress.Report(client.State.ToString());
            return result;
        });
    }

Seguro que hay alguna manera más, cuando la encuentre os la contaré!

Descarga el código de ejemplo de Codeplex

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s