viernes, 2 de julio de 2010

Comunicando Android y Web Services .NET

Android_NET

En esta ocasión el objetivo del post será el de comunicar dos tecnologías totalmente complementarias: Google Android yMicrosoft .NET, para ello nos basaremos en el protocolo SOAP, más concretamente en Web Services desde el lado Microsoft y la creación de un componente personalizado que permitirá consumir y acceder a los datos del modelo de negocio desde un dispositivo Android.

Estudiaremos los casos más sencillos para comunicar un servicio web y un dispositivo móvil, desde la interacción de datos simples hasta estructuras de datos complejas definidos por el desarrollador.

En el ejemplo que vamos a abordar el sistema comprobará un par usuario y contraseña y nos informará si el usuario en cuestión se encuentra registrado dentro del repositorio de members de nuestra aplicación. La arquitectura que entrará en juego será la siguiente, partiremos primeramente de un servicio web que será el encargado de albergar la lógica de comprobación y autenticación de usuario junto con un cliente, en este caso un dispositivo móvil que será el encargado de consumir dicho servicio e informar al usuario del resultado de la operación.

 

Definiendo el Web Services .NET

No profundizaremos demasiado en este aspecto puesto que no es el objetivo del post, partiremos de una clase de tipo “System.Web.Services.WebService” en la que incluiremos un método “EsUsuarioValido” que nos devolverá un objeto de tipo “Usuario” si la autenticación se ha realizado con éxito. Nos debería de quedar algo así:

[WebMethod]
public Usuario EsUsuarioValido(string username, string password)
{
    Usuario reply = null;

    using (Contexto ctx = new Contexto())
    {
        reply = new CADUsuario(ctx).ObtenerUsuario(username, password);
    }

    return reply;
}

La clase Usuario nos debería quedar algo así:

namespace WebService.Entidades
{
    public class Usuario
    {
        …

        private string username;

        public string Username
        {
            get { return username; }
            set { username = value; }
        }
        private string password;

        public string Password
        {
            get { return password; }
            set { password = value; }
        }

        …

    }
}

Ya solo nos quedaría realizar el despliegue y publicarlo en un sitio visible para ser consumido desde cualquier cliente. El siguiente paso que estudiaremos será el desarrollo de la capa del cliente, encargada de consumir y comunicarse con nuestro servicio web, en este caso el dispositivo móvil elegido es Android sobre la versión1.6 aunque también funcionaría en versiones superiores 2.2

 

Consumiendo el servicio web .NET desde Android

Para empezar sera necesario descargar el paquete ksoap2-android de la siguiente dirección: http://code.google.com/p/ksoap2-android/downloads/list, ya solo queda incluirlo en nuestra aplicación Android (http://developer.android.com/guide/index.html, si os queréis iniciar está bastante completa), en el caso de estar trabajando con el IDE Eclipse nos situamos con el botón derecho sobre las propiedades del proyecto y añadimos la referencia bajo la opción Java Build Path.

En la estructura de nuetro proyecto nos encontraremos dos clases, una de ellas será Usuario y otra a la que llamaremos Seguridad. La primera solo la utilizaremos como objeto de transporte de datos y la segunda será la encargada de manejar y contendrá los métodos propios de acceso a la autenticación, actuará de proxy en la comunicación con el servicio web.

Os dejo el ejemplo de la clase de datos Usuario, esto es una lata pero ya sabéis que hablamos de Java así que tendremos que picar algo más de código, Microsoft nos malacostumbra a hacerlo todo con el ratón:

package jcantos.demo.entidades;

import java.util.Hashtable;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

public class Usuario implements KvmSerializable {
    private int idUsuario;
    private String nombre;
    private String email;
    private String username;
    private String password;

    public Usuario(){
        PI_idUsuario.setName("IdUsuario");
        PI_nombre.setName("Nombre");
        PI_email.setName("Email");
        PI_username.setName("Username");
        PI_password.setName("Password");

        PI_idUsuario.setType(PropertyInfo.INTEGER_CLASS);
        PI_nombre.setType(PropertyInfo.STRING_CLASS);
        PI_email.setType(PropertyInfo.STRING_CLASS);
        PI_username.setType(PropertyInfo.STRING_CLASS);
        PI_password.setType(PropertyInfo.STRING_CLASS);
    }
    public void setIdUsuario(int idUsuario) {
        this.idUsuario = idUsuario;
    }

    public int getIdUsuario() {
        return idUsuario;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getNombre() {
        return nombre;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }
    private static PropertyInfo PI_idUsuario = new PropertyInfo();
    private static PropertyInfo PI_nombre = new PropertyInfo();
    private static PropertyInfo PI_email = new PropertyInfo();
    private static PropertyInfo PI_username = new PropertyInfo();
    private static PropertyInfo PI_password = new PropertyInfo();
    private static PropertyInfo[] PI_PROP_ARRAY =
    {
        PI_idUsuario,
        PI_nombre,
        PI_email,
        PI_username,
        PI_password
    };

    @Override
    public Object getProperty(int param) {
        Object object = null;
        switch(param)
        {
            case 0 : object = new Integer(idUsuario);break;
            case 1 : object = nombre;break;
            case 2 : object = email;break;
            case 3 : object = username;break;
            case 4 : object = password;break;
        }
        return object;    }

    @Override
    public int getPropertyCount() {
        return 5;
    }

    @Override
    public void getPropertyInfo(int param, Hashtable arg1, PropertyInfo propertyInfo) {
        switch(param){
            case 0:
                propertyInfo.type = PropertyInfo.INTEGER_CLASS;
                propertyInfo.name = "IdUsuario";
                break;          
            case 1:
                propertyInfo.type = PropertyInfo.STRING_CLASS;
                propertyInfo.name = "Nombre";
                break;          
            case 2:
                propertyInfo.type = PropertyInfo.STRING_CLASS;
                propertyInfo.name = "Email";
                break;          
            case 3:
                propertyInfo.type = PropertyInfo.STRING_CLASS;
                propertyInfo.name = "Username";
                break;          
            case 4:
                propertyInfo.type = PropertyInfo.STRING_CLASS;
                propertyInfo.name = "Password";
                break;          
        }
    }

    @Override
    public void setProperty(int param, Object obj) {
        switch(param)
        {
            case 0  : idUsuario     = ((Integer)obj).intValue(); break;
            case 1  : nombre     = (String)obj; break;
            case 2  : email     = (String)obj; break;
            case 3  : username     = (String)obj; break;
            case 4  : password     = (String)obj; break;
        }  
    }
}

Me parece algo tosco para indicar a una clase que es Serializable, en .NET simplemente marcaríamos como atributo [Serializable] pero bueno “eso es así”. Y ahora os dejo la clase correspondiente al consumo del web services.

package jcantos.demo.utiles;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.util.Log;
import jcantos.demo.entidades.*;

public class Seguridad {
    private static final String METHOD_NAME = "EsUsuarioValido";
    private static final String NAMESPACE = "
http://tempuri.org/";
    private static final String URL = "http://www.jcantos.net/webservice.asmx"; //sustituir esta URL por el sitio definitivo
    private static final String SOAP_ACTION = "
http://tempuri.org/EsUsuarioValido";

    private static Usuario usuario=null;
    public static Usuario EsUsuarioValido(String username, String password){
        Usuario reply=null;
        try {
             SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
             request.addProperty("username", username);
             request.addProperty("password", password);
             SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
             envelope.dotNet=true;
             envelope.setOutputSoapObject(request);
             HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
             androidHttpTransport.call(SOAP_ACTION, envelope);
             SoapObject result = (SoapObject)envelope.getResponse();
             if (result!=null){
                    reply=new Usuario();
                 reply.setIdUsuario(Integer.parseInt(result.getProperty("IdUsuario").toString()));
                 reply.setNombre(result.getProperty("Nombre").toString());
                 reply.setEmail(result.getProperty("Email").toString());
                 reply.setUsername(username);
                 reply.setPassword(password);
             }
         } catch (Exception e) {
             Log.e("Servicio_Web",e.getMessage());
         }
        return reply;
    }

    public static void setUsuario(Usuario usuario) {
        Seguridad.usuario = usuario;
    }

    public static Usuario getUsuario() {
        return usuario;
    }
    public static boolean EsLogueado(){
        return Seguridad.usuario!=null;
    }
}

Me parece un tema interesantísimo, imaginaros las posibilidades que se nos abre y el abanico de oportunidades para integrar vuestros cacharritos con aplicaciones ya desarrolladas, no solamente en .NET sino cualquier otro lenguaje JAVA por ejemplo,……¿quién nos quita que pudiésemos elaborar un servicio web a modo de gateway que expusiese aquellos métodos que quisieran ser consumidos por un dispositivo móvil?. Y no solo hablamos a modo de consulta, aquí os dejo un ejemplo en el que podemos actualizar la clave de un usuario una vez que la autenticación se ha realizado con exito, básado en el ejemplo anterior.

Editando y actualizando objetos de negocio desde el dispositivo móvil

Supongamos que un usuario ha realizado una operación de login con éxito y desea cambiar o actualizar su perfil, informando al sistema central del cambio de su correo electrónico junto con la actualización de su nueva contraseña. Para ello podríamos tener una clase tal que esta:

package jcantos.demo.cad;

import jcantos.demo.entidades.Usuario;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.util.Log;

public class CADUsuario {
    private static final String METHOD_NAME = "ActualizarUsuario";
    private static final String NAMESPACE = "
http://tempuri.org/";
    private static final String URL = "http://www.jcantos.net/webservice.asmx";
    private static final String SOAP_ACTION = "http://tempuri.org/ActualizarUsuario";

    public void ActualizarUsuario(Usuario usuario){
        try {
             SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
             PropertyInfo pi=new PropertyInfo();
             pi.setName("usuario");
             pi.setValue(usuario);
             pi.setType(usuario.getClass());
             request.addProperty(pi);
             SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
             envelope.dotNet=true;
             envelope.setOutputSoapObject(request);
             envelope.addMapping(NAMESPACE, "Usuario", usuario.getClass());
             HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
             androidHttpTransport.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
             androidHttpTransport.debug=false;
             androidHttpTransport.call(SOAP_ACTION, envelope);
             envelope.getResponse();
         } catch (Exception e) {
             Log.e("Servicio_Web",e.getMessage());
         }
    }
}

Como veis las posibilidades son infinitas, así que una vez más que la técnica no coarte las necesidades del cliente. Un abrazo a todos y nos vemos en el próximo post.

Related Posts Plugin for WordPress, Blogger...