.NET SoapClient: simple cliente para servicios SOAP

Ya hace varios años que se pusieron de moda las Web APIs, pero hasta la fecha, los servicios por medio del protocolo SOAP siguen siendo de lo más usado. En .NET tenemos los Web Services (ASMX) y WCF, que permiten crear servicios bajo este protocolo. La facilidad de crearlos y consumirlos en este framework es parte de lo que los hace tan populares hasta hoy en día.

Hacer llamadas al servidor desde el back-end puede ser muy tedioso, y la ventaja de Visual Studio respecto a esto es que nos da la facilidad de agregar como “referencia web” un servicio, con lo cual, basado en su WSDL, se generan todas las clases requeridas para la comunicación con el servicio.

Personalmente, odio las referencias web. Desde mi primer proyecto hace casi una década he tenido que trabajar con esta “ventaja” de .NET, y la verdad es que se puede tornar muy complicado. Aquí describo las que considero como principales desventajas (o si lo prefieres, puedes ir directo a la solución):

Contenido extra en la aplicación

Precisamente en mi primer proyecto profesional me topé con esto. El producto era un sistema para registro de multas de tránsito. Se usaría un sistema en Windows Mobile (no Windows Phone, aclaro) con el que se capturarían las multas, las cuales se enviaban a un servicio web (ASMX) para almacenarlos en la base de datos. Una aplicación de escritorio de Windows también se conectaría con el servicio para extraer la información de las multas registradas, además de otras operaciones.

Era muy fácil crear el servicio web y añadir la referencia en las dos aplicaciones para consumirlo. El problema es que esto crea una serie de clases, con base en el WSDL, y las incluye en la aplicación. De aquí, nosotros tenemos que mapear a las clases específicas de nuestra aplicación. No importa si los tres proyectos comparten una misma biblioteca de clases. Si el servicio web usa una clase MarcaDeVehiculo, al agregar la referencia a una aplicación, se crea una clase nueva, para uso de la misma referencia. Prácticamente, todo nuestro esquema de entidades estaba duplicado en la aplicación móvil y la de escritorio.

Otra cosa es que se generan las clases de todo el esquema definido en el WSDL. Si agregamos un servicio que tiene veinte operaciones de las que solo usaremos tres, se crearán las clases y métodos de solicitud y respuesta de las veinte operaciones.

Cuando el servicio web es desarrollo propio, el mantenimiento de estas clases también es un problema. Si en algun punto decides cambiar una clase de las utilizadas por el servicio (agregar o modificar una propiedad), tienes que actualizar la referencia para que se regeneren todas estas clases.

Además, está el factor de rendimiento: uno debe ser muy cuidadoso en el código, y hacer que el sistema sea lo más eficiente posible. Pero esto es más de cuidado cuando nuestro objetivo son dispositivos de bajos recursos, como lo eran las PocketPCs con Windows Mobile de hace diez años. Que todas las clases de entidades estén duplicadas es un costo extra para el dispositivo. Y que la tecnología actual sea exponencialmente más poderosa cada año no significa que debamos darnos el lujo de llenar nuestro código de componentes innecesarios.

Configuración de los endpoints

Esto me ha sucedido en todos los proyectos que se comunican con servicios por medio de referencia web, hasta la fecha. Al agregar una referencia de este tipo, el archivo de configuración (app.config o web.config) se modifica para incluir las rutas de acceso al servicio (endpoints). Las clases cliente generadas al crear la referencia utilizan dicha configuración para hacer las solicitudes a la ruta correcta.

El problema es cuando queremos cambiar los endpoints. Si es un servicio de desarrollo propio, seguramente tendremos varios ambientes desde los cuales consumir el servicio, ya sea local, en un servidor de pruebas, en un servidor de producción, etc. Si es un servicio de terceros, algunos proveedores nos dan un acceso para desarrollo y uno diferente para producción. Siendo que la URL al servicio está dentro del archivo de configuración, puede parecer sencillo hacer el cambio a mano y que todo funcione sin ningun problema, pero modificar archivos en el sitio es muy delicado. Un descuido y podemos estar dirigiendo las peticiones a una dirección incorrecta.

Era todavía peor en el caso de Windows Mobile, ya que la configuración estaba dentro del archivo compilado. Imagínense enviar el sistema desde Sonora hasta el cliente en Jalisco para que hicieran las instalaciones; que el sistema no funcionara; tener que viajar hasta allá para revisarlo, y descubrir que el problema era que la aplicación no podía llamar al servicio en http://localhost:63425. Más allá de cambiar la ruta, la solución era montar el servicio web en su servidor, hacerlo público, actualizar la referencia desde Visual Studio, volver a generar el instalador móvil y finalmente instalarlo en los dispositivos.

SoapClientService: Consumir servicios en .NET sin usar referencias web

Aclaro: todo tiene sus pros y sus contras. No estoy proponiendo una solución absoluta que resuelva los problemas de las llamadas a servicios desde .NET, o que mi solución esté exenta de errores (ojalá, pero no puedo jurarlo).

Las Web APIs están siendo muy populares, principalmente con el uso de llamadas asíncronas desde aplicaciones web. Hacer solicitudes a un servidor por medio de JavaScript y JSON es muy sencillo, por la facilidad del lenguaje para procesar los datos recibidos desde sus mismas APIs ya integradas. En el back-end de .NET es algo más complejo, aunque el uso de herramientas como HttpClient lo hacen más sencillo.

HttpClient es una clase que permite hacer peticiones por medio de red, y el contenido puede ser recibido como texto o Stream. Requiere cierta configuración, algo más a detalle que un XmlHttpRequest de JavaScript, pero sigue siendo sencillo. Lo que es complicado es el manejo de XML, necesario para enviar y recibir los mensajes usando el protocolo SOAP. La serialización y deserialización puede ser algo difícil, y es lo que más trabajo me costó. Pero finalmente creo que logré el resultado: un cliente genérico para realizar peticiones a un servicio web con el protocolo SOAP.

Explicaré lo que hago en el proyecto y cómo funciona este componente:

La clase SoapClient es la encargada de realizar las peticiones al servicio. Su constructor recibe dos parámetros: la url del servicio web y el namespace, el cual se encuentra en el WSDL del mismo, como atributo targetNamespace del nodo raíz. Cuando creamos un servicio ASMX en .NET, el namespace es http://tempuri.org/, pero lo podemos (y deberíamos) cambiar como mejor se ajuste a nuestro sistema.

La clase tiene los métodos públicos Post, Post, PostAsync y PostAsync. Todos hacen lo mismo, todos reciben los mismos parámetros. La diferencia es que los dos últimos son asíncronos, y dos de ellos devuelven el resultado de la solicitud convertido a un objeto de tipo T. La clase definida aquí deberá ser equivalente a lo que el método web devuelva.

Ya que las solicitudes a los métodos SOAP se hacen por medio de POST, no estoy incluyendo GET, ni otros métodos.

Los parámetros que reciben son el nombre del método web y un objeto opcional que contenga los parámetros para el método web. Este objeto puede ser de una clase concreta o anónima. Esto fue lo que más trabajo me llevó, ya que el serializador de XML en .NET no soporta objetos anónimos, y tuve que hacer uno a mano, basado en lo que encontré aquí.

Ejemplo

// WebService.asmx (http://tempuri.org/myservice)
[WebMethod()]
public CommonLibrary.User FindUser(string name, string email) {
    return SearchUser(name, email);
}
// Application
using (SoapClient client = new SoapClient(
    "http://mysite.com/WebService.asmx",
    "http://tempuri.org/myservice/")) {
    CommonLibrary.User user = client.Post(
        "FindUser", // WebMethod name
        new {
            name = userName,
            email = userEmail
        });
}

Eso es todo. El método PostAsync se encargará de construir la solicitud usando el namespace y el nombre del método, construirá el mensaje en XML usando el segundo parámetro (o lo enviará vacío si éste es nulo), enviará la solicitud, y una vez recibido el resultado, lo deserializará de XML al tipo solicitado. El resultado puede ser un tipo nativo o una clase compleja.

Con esto ya podemos crear servicios y aplicaciones que compartan el mismo esquema de modelos, y no habrá la necesidad de que Visual Studio genere las clases que necesita la referencia web. El mantenimiento de nuestro código será más fácil. Los datos de referencia (URL al servicio y namespace) pueden ser obtenidos por cualquier medio, ya sea por los appSettings del archivo de configuración, desde una base de datos o de donde lo necesitemos.

Anuncios

Autor: Israel Muñoz

Soy desarrollador de software, principalmente dedicado a desarrollo de aplicaciones web. Especializado en .NET full-stack, además de tecnologías front-end HTML, CSS y JavaScript. A ratos, profesor de materias de informática. Me gusta mucho todo lo que tiene que ver con las tecnologías nuevas para desarrollo web, y el diseño de sitios y aplicaciones.

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 )

Google+ photo

Estás comentando usando tu cuenta de Google+. 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 )

w

Conectando a %s