jueves, 10 de octubre de 2013

Servicios web (webservices) con SOAP sobre CodeIgniter

(Continuación del artículo Servicios web (webservices) con REST sobre CodeIgniter)

SOAP

En la primera parte de este artículo te comenté sobre el uso de REST sobre CodeIgniter usando la biblioteca “CodeIgniter Rest Server”. A continuación de hablaré sobre el trabajo con la biblioteca nusoap para la implementación de webservices con SOAP sobre CodeIgniter.

La nusoap (versión 0.9.5) puedes descargarla en [2]. En el artículo [1] puedes ver un guía excelente de la instalación de nusoap sobre CodeIgniter. Básicamente es igual a la instalación del Rest Server: copias la carpeta lib de la instalación de nusoap para la carpeta application/libraries (en mi caso le cambié el nombre a la carpeta lib por nusoap_lib), y en ese mismo nivel creas una biblioteca interfaz que hace una llamada a la nusoap, así:

class Nusoap_library
{
function Nusoap_library()
{
require_once('nusoap_lib/nusoap'.EXT);
}
}

Sencilla de instalar. Ahora vamos a crear servicios: siguiendo el mismo tutorial [1] hacemos un controlador con su constructor, un index y un servicio que suma dos números:

class Soapserver extends CI_Controller
{
function Soapserver()
{
parent::__construct();
$this->load->library("Nusoap_library"); //cargando mi biblioteca
$this->nusoap_server = new soap_server();
$this->nusoap_server->configureWSDL("SOAP Server", $ns);
$this->nusoap_server->wsdl->schemaTargetNamespace = $ns;

//registrando funciones
$input_array = array ('a' => "xsd:string", 'b' => "xsd:string");
$return_array = array ("return" => "xsd:string");
$this->nusoap_server->register('addnumbers', $input_array, $return_array, "urn:SOAPServerWSDL", "urn:".$ns."/addnumbers", "rpc", "encoded", "Addition Of Two Numbers");
}

function index()
{
function addnumbers($a,$b)
{
$c = $a + $b;
return $c;
}

$this->nusoap_server->service(file_get_contents("php://input"));
}
}

Solamente tenemos dos métodos principales, el constructor y el index. En el constructor cargo mi biblioteca que a su vez llama a la nusoap, y configuro el servidor de nusoap. A continuación registro la función addnumbers (debajo del comentario “registrando funciones”). Ese grupo de código dice que la función recibe dos variables strings llamadas a y b, y que retorna otro string. Con $this->nusoap_server->register se registra la función especificando los parámetros de entrada y salida y un conjunto de parámetros que ahora no tienen importancia para este artículo. El último de ellos es un comentario o documentación del servicio addnumbers. Podrás ver que la función se implementa dentro del index, y después de esta función (y de todas las que adiciones) llamas a $this->nusoap_server->service.

Hasta aquí el servidor SOAP debe funcionar sin problemas. Podrás ver tus servicios publicados accediendo a tu sitio http://sitio/index.php/soapserver. Para consumir los mismos prepara otro controlador (por ejemplo soapserver_testing) y un método como este:

function test()
{
$params = array(
'a' => 2,
'b' => 3,
);

$result = $this->nusoap_client->call('addnumbers', $params);
}

Al cual accederías normalmente por el link http://sitio/index.php/soapserver_testing/test.

El problema que nos presenta SOAP sobre REST es que hay que declarar las variables de entrada y salida de cada función, y registrar cada función, lo cual es más pesado pero por supuesto genérico (recuerda que REST formatea cualquier resultado de BD directamente hacia un XML, JSON, etc. sin importar si los valores son enteros, cadenas, etc).

El gran problema con SOAP es a la hora de retornar consultas de la BD que implica la existencia de arreglos, objetos, tipos de datos complejos más allá de los string e int… para esto, debes construir la estructura de los objetos que retornarás. En el siguiente código te pongo un ejemplo completo, por pasos:

1. Creamos una clase que será el resultado del servicio. Utilizando la misma que creé para los servicios REST como te puse en la primera parte de este artículo, sería algo así (fuera de la clase Soapserver):

class Response_students
{
public $responseCode = 100;
public $responseMessage = 'Error desconocido';
public $data = NULL;
}

2. Declaramos un tipo de dato “Student” en el constructor de Soapserver (debajo del comentario “registrando funciones”):

$this->nusoap_server->wsdl->addComplexType(
"Student", //nombre del tipo de dato
"complexType", //tipo complejo
"struct", //en este caso struct por ser un objeto
"all", //no interesante ahora
"", //no interesante ahora
array( //miembros de la clase Student (columnas en la BD)
"NAME"=>array("name"=>"NAME", "type"=>"xsd:string"),
"AGE"=>array("name"=>"AGE", "type"=>"xsd:int")
)
);

3. Creamos un tipo de dato que será un arreglo de “Students”:

$this->nusoap_server->wsdl->addComplexType(
'Students_array',
'complexType',
'array', //tipo array porque es un arreglo de objetos
'',
'SOAP-ENC:Array', //estructura equivalente en SOAP
array(),
array(
array(
'ref' => 'SOAP-ENC:arrayType',
'wsdl:arrayType' => 'tns:Student[]' //contiene un array de Student
)
),
'tns:Student
);

4. Creamos el objeto respuesta de mi servicio, correspondiente a la clase Response_students del paso 1. La clase contiene un código que es un número, un mensaje string, y un objeto de tipo students_array creado en 3, que a su vez es un arreglo de Student creado en 2:

$this->nusoap_server->wsdl->addComplexType(
'Response_students',
'complexType',
'struct',
'all',
'',
array
('responseCode' => array('type' => 'xsd:int'),
'responseMessage' => array('type' => 'xsd:string'),
'data' => array('type' => 'tns: Students_array')
)
);

5. Registramos un método que va a devolver los estudiantes de un aula (class_room):

$this->nusoap_server->register(
"get_students", //nombre de la función
array('class_room' => "xsd:int"), //parámetros de entrada
array("return" => "tns: Response_students"), //objeto de salida
$ns,
$ns . "#get_students",
"rpc",
"encoded",
"Servicio que retorna los estudiantes de un aula"
);

6. Implementamos la función-servicio que retorna los estudiantes, dentro del index:

function get_students($class_room)
{
$CI = &get_instance();
$CI->load->model('students_model');
$students = $CI->students_model->get_students_of_room($class_room);

$result = new Response_students();

if (!empty($students))
{
$result->responseCode = 0;
$result->responseMessage = 'Estudiantes encontrados';
$result->data = array();
foreach ($students as $value)
$result->maintenances_data[] = $value;
}
return $result;
}

Listo. Recuerda que esto no es un tutorial, por eso no he explicado a profundidad todo el código. Basta con que te hayas llevado la idea de cómo es el funcionamiento de la biblioteca nusoap.

Espero que te hayan servido ambas partes del artículo, la relacionada con REST y esta con SOAP. Mi principal conclusión es que realmente hacen honor a los comentarios que te encuentras en internet: con REST implementas rápidamente cualquier servicio y él se encarga de formatearlo a XML u otras estructuras, pero es poco genérico; SOAP es más genérico pero es realmente complicado usarlo para estructuras complejas. De todas maneras, según el sistema que quieras implementar, las bibliotecas que te describí te pueden ser útiles para seguir incorporando funcionalidades a tus sitios implementados sobre CodeIgniter.

Referencias:

[1] SOAP Server In CodeIgniter using NuSOAP PHP Toolkit.htm

http://www.php-guru.in/2013/soap-server-in-codeigniter-using-nusoap-library/

[2] Descarga de biblioteca nusoap:

http://sourceforge.net/projects/nusoap/


¿Te ha gustado este Post? Compártelo con tus amigos.

2 comentarios:

  1. Hola.
    La clase no tiene esta propiedad, si no data, según la defines más arriba.

    $result->maintenances_data[] = $value;

    Sabes porqué puede estar pasando esto al llamar al servicio?

    Array
    (
    [faultcode] => SOAP-ENV:Client
    [faultactor] =>
    [faultstring] => method 'Method'('Method') not defined in service('' '')
    [detail] =>
    )

    Gracias.

    ResponderEliminar
    Respuestas
    1. Necesitas poner el nombre de tu controlador antes del metodo, en medio de los dos debe llevar dos puntos: nombre_controlador..nombre_metodo

      Eliminar

IconIconIcon