10 may 2010

Servicios Web – Retornar un XML con NuSOAP

Aquí estoy nuevamente con un artículo de web services / servicios web, en esta oportunidad, voy a incluir un ejemplo en el cual ser servidor retorna un XML al cliente, y este lo muestra por pantalla. Este ejemplo surgió a petición de un visitante a mi blog. Espero que les sea de utilidad.

Servidor

El servidor es muy similar a todos los servidores de los ejemplos anteriores, simplemente que retorna un XML. Dado que el servidor retorna un XML, debemos convertirlo antes de poder retornarlo, para que no genere conflictos con el código del “envoltorio” (SOAP) que transportará el XML. La forma más rápida es utilizar el método base64_encode que ya trae incorporado PHP. Es decir, codificamos el XML con base64, de esta forma nos aseguramos que no aparecerán caracteres extraños, y luego retornamos ese XML modificado.

<?php
require_once('nusoap/lib/nusoap.php');
$miURL = 'http://pruebas.orlandobrea.com.ar/nusoap_ejXML';
$server = new soap_server();
$server->configureWSDL('ws_orlando', $miURL);
$server->wsdl->schemaTargetNamespace=$miURL;

$server->register('getXML', // Nombre de la funcion
                   array(), // Parametros de entrada
                   array('return' => 'xsd:string'), // Parametros de salida
                   $miURL);
function getXML(){
    return new soapval('return', 'xsd:string', base64_encode('<?xml version="1.0" encoding="UTF-8"?>
<vehiculos>
  <coche>
      <marca>Toyota</marca>
      <modelo>Corolla</modelo>
      <fechaCompra>2002</fechaCompra>
  </coche>
  <coche>
      <marca>Honda</marca>
      <modelo>Civic</modelo>
      <fechaCompra>2003</fechaCompra>
  </coche'));
}

$server->service($HTTP_RAW_POST_DATA);
?>

En el ejemplo el XML es fijo y esta incorporado a la función del web services, en la vida real, el código XML lo generaremos a partir de algunos datos dinámicos o en como necesitemos.

Cliente

El cliente es casi el mismo que en los ejemplos iniciales que he dado en mi blog, salvo que antes de mostrar/procesar la respuesta, se debe transformar de base64 a texto normal, algo así como “descodificarla”. Para esto PHP también nos provee de una función base64_decode.

<?php
require_once('nusoap/lib/nusoap.php');
// Crear un cliente apuntando al script del servidor (Creado con WSDL)
$serverURL = 'http://pruebas.orlandobrea.com.ar';
$serverScript = 'nusoap_server_ejXML.php';
$metodoALlamar = 'getXML';

$cliente = new nusoap_client("$serverURL/$serverScript?wsdl", 'wsdl');
// Se pudo conectar?
$error = $cliente->getError();
if ($error) {
    echo '<pre style="color: red">' . $error . '</pre>';
    echo '<p style="color:red;'>htmlspecialchars($cliente->getDebug(), ENT_QUOTES).'</p>';
    die();
}

// 1. Llamar a la funcion getRespuesta del servidor
$result = $cliente->call(
    "$metodoALlamar",                     // Funcion a llamar
    array(),    // Parametros pasados a la funcion
    "uri:$serverURL/$serverScript",                   // namespace
    "uri:$serverURL/$serverScript/$metodoALlamar"       // SOAPAction
);
// Verificacion que los parametros estan ok, y si lo estan. mostrar rta.
if ($cliente->fault) {
    echo '<b>Error: ';
    print_r($result);
    echo '</b>';
} else {
    $error = $cliente->getError();
    if ($error) {
        echo '<b style="color: red">-Error: ' . $error . '</b>';
    } else {
        echo base64_decode($result);


    }
}

?>

En el ejemplo, solo muestro la respuesta que me ofreció el servidor del servicio web, cuando estemos ante una implementación real, seguramente deberemos procesar esta información para ofrecer, por ejemplo, una respuesta a un usuario.

Conclusión

Como hemos podido ver, la implementación de este tipo de alternativas, es muy sencilla, solo debemos tener especial cuidado en la codificación de la respuesta que utilizamos en NuSOAP. A modo de “regla” podría decir que todo aquello que sabemos que utilizará caracteres extraños, podemos codificarlo en base 64 en el servidor, y luego decodificarlo en el cliente (de esta forma nos aseguramos que la información ha viajado correctamente).

Debo reconocer que hace tiempo que no indago el código de NuSOAP, por lo tanto no recuerdo si codifica de alguna forma las respuestas automáticamente (que yo recuerde, no lo hace).

Espero que haya sido de utilidad, y que sirva para que sigan explorando con los Web Services.

Saludos a todos los seguidores, y gracias!

3 may 2010

WebServices con NuSOAP en PHP - Ejemplo 4 - Vista

Bueno, estoy volviendo al blog de a poco, es por eso que estoy publicando la 4 parte de los ejemplos de web services con NuSOAP. En esta oportunidad voy a poner un simple ejemplo de cliente/servidor en el cual el cliente muestra la información enviada por el servidor.

Si bien el código es muy sencillo, y parecido a los ejemplos anteriores, puede ser un poco más complejo de seguir, ya que hay una parte de visualización de los datos obtenidos desde el servidor (a diferencia de los ejemplos anteriores).

Es importante aclarar que esta forma de desarrollo no es la más aconsejable para un desarrollo real, ya que la vista esta muy ligada al código de conexión. Todo mejoraría un poco más utilizando algún sistema de templates, como smarty, twig, etc.

¿Como funciona el ejemplo?
El cliente llama a listarClientes en el servidor, y muestra el resultado retornado en una tabla. Cuando el usuario hace click para ver los datos de un determinado cliente, el cliente (PHP) llama a getCliente, que retorna todos los datos del cliente. Luego, muestra todos los datos obtenidos del servidor al usuario.

Comencemos por el servidor

<?php

require_once('nusoap/lib/nusoap.php');
require_once('cliente.php');
require_once('clienteDAO.php');
require_once('soporte_obrea.php');

$miURL = 'http://pruebas.orlandobrea.com.ar/nusoap';
$server = new soap_server();
$server->configureWSDL('ws_orlando', $miURL);
$server->wsdl->schemaTargetNamespace=$miURL;

$soporteWS = new SoporteWS(); // Creo una instancia del objeto propio (SoporteWS)

// ---------------- Funciones usadas por el WS
function getCliente($id) {
    global $soporteWS;
    $dao = new ClienteDAO();
    $cliente = $dao->getCliente($id);
    $respuesta = $soporteWS->convertirAVectorParaWS($cliente);
    return new soapval('return', 'tns:Cliente', $respuesta);
}
function listarClientes() {
    $dao = new ClienteDAO();
    $listado = $dao->getList();
    $objSoporte = new SoporteWS();
    $respuesta = $objSoporte->convertirAVectorParaWS($listado);                 
    return new soapval('return', 'tns:listadoClientes', $respuesta);
}

// --------------- Definicion de las funciones y estructuras expuestas en el WS
$server->wsdl->addComplexType('Cliente',
    'complexType',
    'struct',
    'all',
    '',
    array(
        'id' => array('name' => 'id', 'type' => 'xsd:int'),
        'nombre' => array('name' => 'nombre',    'type' => 'xsd:string'),
        'apellido' => array('name' => 'apellido', 'type' => 'xsd:string'),
        'cuit' => array('name' => 'CUIT',    'type' => 'xsd:string')
    )
);
$server->wsdl->addComplexType('listadoClientes',
    'complexType',
    'array',
    '',
    '',
    array (array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'tns:Cliente[]'))
);

$server->register('getCliente', // Nombre de la funcion
                   array('id' => 'xsd:int'), // Parametros de entrada
                   array('return' => 'tns:Cliente'), // Parametros de salida
                   $miURL
                 );
$server->register('listarClientes', // Nombre de la funcion
                   array(), // Parametros de entrada
                   array('return' => 'tns:listadoClientes'), // Parametros de salida
                   $miURL
                 );

$server->service($HTTP_RAW_POST_DATA);
?>


El servidor es muy similar a los anteriores, expone solo dos métodos:

  • listarClientes: retorna una lista con los clientes.
  • getCliente: retorna el cliente cuyo id es recibido como parámetro de llamada.
La definición del objeto, y los métodos de acceso a datos (DAO) están definidos en los archivos que son importados (cliente.php y clienteDAO.php). Recomiendo que te descargues el archivo comprimido con todo los archivos, que se encuentra al final de este tutorial y que prestes atención a estos archivos. Si bien son muy sencillos, te ayudará mucho que vayas realizando pruebas sobre los mismos también para que puedas ver el comportamiento. Si solo te interesa la parte de WS (Web Services), puedes prestar atención solo a los archivos que están publicados.

¿Como es el cliente?
Bueno, el cliente es un poquito más complejo que los anteriores, ya que no solo hace peticiones al servidor, sino que tiene una pequeña interacción y presenta los datos al usuario.


<?php
require_once('nusoap/lib/nusoap.php');
require_once('cliente.php');
require_once('soporte_obrea.php');

// Crear un cliente apuntando al script del servidor (Creado con WSDL)
$serverURL = 'http://pruebas.orlandobrea.com.ar'; // Reemplazar por la URL de tu servidor
$serverScript = 'nusoap_server_ej4_view.php';
$soporteWS = new SoporteWS();

$cliente = new nusoap_client("$serverURL/$serverScript?wsdl", 'wsdl');
// Se pudo conectar?
$error = $cliente->getError();
if ($error) {
    echo '<pre style="color: red">' . $err . '</pre>';
    echo '<p style="color:red;'>htmlspecialchars($client->getDebug(), ENT_QUOTES).'</p>';
    die();
}

$listado = '';
$objCliente = '';
// Existe el parametro "view" en la URL con la cual se esta llamando a este script?
if ($_GET['view']) {
    // Si => debo mostrar los datos del cliente
    $metodoALlamar = 'getCliente';
    $result = $cliente->call(
        $metodoALlamar,                     // Funcion a llamar
        array($_GET['id']),    // Parametros pasados a la funcion
        "uri:$serverURL/$serverScript",                   // namespace
        "uri:$serverURL/$serverScript/$metodoALlamar"       // SOAPAction
    );
    // Verificacion que los parametros estan ok, y si lo estan. mostrar rta.
    if ($cliente->fault) {
        echo '<b>Error: ';
        print_r($result);
        echo '</b>';
    } else {
        $error = $cliente->getError();
        if ($error) {
            echo '<b style="color: red">Error: ' . $error . '</b>';
        } else {
            $objCliente = $soporteWS->convertirDeVectorDesdeWS($result, 'Cliente');
         }
    }
} else { // No fue llamado con el parametro "view" en la URL => debo mostrar el listado de clientes
    $metodoALlamar = 'listarClientes';
    $result = $cliente->call(
        $metodoALlamar,                     // Funcion a llamar
        array(),    // Parametros pasados a la funcion
        "uri:$serverURL/$serverScript",                   // namespace
        "uri:$serverURL/$serverScript/$metodoALlamar"       // SOAPAction
    );
    // Verificacion que los parametros estan ok, y si lo estan. mostrar rta.
    if ($cliente->fault) {
        echo '<b>Error: ';
        print_r($result);
        echo '</b>';
    } else {
        $error = $cliente->getError();
        if ($error) {
            echo '<b style="color: red">Error: ' . $error . '</b>';
        } else {
            $listado = $soporteWS->convertirDeVectorDesdeWS($result, 'Cliente');
         }
    }
}
// De aca en mas es la pagina HTML que muestra el listado de clientes o los datos de un cliente en particular (dependiendo
// si se llamo con view como parametro o no)
?>
<html>
<body>
    <? if ($listado) { ?>
    <!-- Listado de todos los clientes -->
    <h1>Listado</h1>
    <table>
        <tr><td>Nombre</td><td>Apellido</td><td>CUIT</td><td>Acciones</td></tr>
        <?php
            foreach ($listado as $objeto) {
                echo '<tr>';
                echo '<td>'.$objeto->nombre.'</td>';
                echo '<td>'.$objeto->apellido.'</td>';
                echo '<td>'.$objeto->cuit.'</td>';
                echo '<td><a href="nusoap_client_ej4_view.php?view=yes&id='.$objeto->id.'">Ver</a></td>';
                echo '</tr>';
            }
        ?>
    </table>
    <? } else { ?>
    <!-- Datos del cliente seleccionao -->
    <h1>Cliente</h1>
    <?php
        echo '<strong>ID:</strong> '.$objCliente->id.'<br/>';
        echo '<strong>Nombre:</strong> '.$objCliente->nombre.'<br/>';
        echo '<strong>Apellido:</strong> '.$objCliente->apellido.'<br/>';
        echo '<strong>CUIT:</strong> '.$objCliente->cuit.'<br/>';
    ?>
    <? } ?>
</body>
</html>


Como mencione anteriormente, el cliente es un poco más complejo que los anteriores, ya que el mismo script permite ver el listado de clientes, y los datos de un cliente en particular.
Si el script, no recibe parámetros en la URL, muestra el listado de todos los clientes que se encuentran en el sistema (llamando a listarClientes en el WS). En cambio si recibe el parámetro view, y el id del cliente que se desea mostrar, ejecuta otra sección del script, la que muestra los datos del cliente.

La página del listado se ve así












Y luego de hacer click sobre "Ver", nos lleva a la página:











la cual contiene el detalle del cliente seleccionado, en este caso "Blas Pascal". Ambas páginas, se encuentran en el mismo script. Es cuestión de gustos incluir ambas en el mismo script, o utilizar dos scripts diferentes. A los fines de este artículo, me pareció más práctico incluir toda la información del cliente en un único script. Si lo deseas, puedes separar el archivo cliente en dos scripts menores.

Los archivos cliente, y servidor los puedes descargar aquí

Conclusión
Este artículo, es una recopilación de los anteriores, con unos pequeños agregados, como la interacción con el usuario, y la presentación de la información.

Este artículo pretende ser el puntapié inicial para que comiences a explorar tu mismo con los servicios web, ya sean en PHP o cualquier otro lenguaje que te guste. Las posibilidades son infinitas, tu serás el que ponga los limites en que aplicación o módulo utilizarás web services. Ya tienes una base para seguir explorando, ahora depende mucho de ti que practiques y comiences a utilizarlos en tus proyectos.

Debo hacer una aclaración que creo que es importante si aún no has comenzado proyectos con Web services. Al comenzar a utilizar web services, te encontrarás con varios inconvenientes, algunos de la herramienta que utilizamos, y otros propios de la etapa de aprendizaje. Lo importante es saber que el camino puede ser un poco difícil en algunos momentos, pero se puede lograr. Si te quedas atascado, sigue probando, descansa un poco, piensa alternativas, busca las posibles causas, y vuelve a retomar. Por eso es tan importante, que no comiences con un proyecto real, sino que, como todo, comiences sobre un proyecto interno, o de prueba, para ir probando y tener tiempo para encontrar los trucos.

Otro punto importante, los Web Services, tampoco son la bala de plata (Silver bullet), por lo tanto, no intentes utilizarlos para cualquier proyecto, aplica tu criterio para decidir que es lo más conveniente para cada proyecto o módulo que debes realizar.

Espero que haya sido de utilidad, me gustaría conocer tu experiencia, si te ha resultado de utilidad este artículo, y que información más te gustaría encontrar sobre estos temas.

Éxitos y a seguir probando.