lunes, 3 de agosto de 2015

Creación de Servicios Web con PHP y SOAP

SOAP es un protocolo de intercambio para servicios web basado en XML, sobre el que puedes leer en esta entrada de la wikipedia. Para no liarnos con preámbulos, vamos al grano ¿cómo creamos un servicio web en PHP?.

Para facilitarnos la vida empezaremos por descargar NuSOAP, un toolkit para el desarrollo de servicios web con SOAP en PHP, que nos proveerá de diversas clases para trabajar con este protocolo. Basta con descargarlo, descomprimirlo, meterlo dentro de nuestro proyecto y, cuando queramos usarlo, incluir nusoap.php como librería.

Con NuSOAP instalado toca crear un servidor SOAP en nuestra aplicación. Para el ejemplo crearemos un servidor, lo llamaremos producto.php, que si recibe una petición donde se le pida una lista de libros devuelva tres títulos (es un ejemplo básico, piensa que en la realiad podrías acceder a una base de datos y dar muchas más funcionalidades).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
    require_once "nusoap.php";
      
        function getProd($categoria) {
        if ($categoria == "libros") {
            return join(",", array(
                "El señor de los anillos",
                "Los límites de la Fundación",
                "The Rails Way"));
        }
        else {
                return "No hay productos de esta categoria";
        }
    }
      
    $server = new soap_server();
    $server->register("getProd");
    $server->service($HTTP_RAW_POST_DATA);
?>


Ok, ahora necesitas un cliente, que llamaremos cliente.php. En el constructor, el cliente recibirá el url del servidor y para acceder al método que nos devuelve los libros recurriremos al método call(), al cual le pasaremos el nombre del método del servidor al que queremos acceder y los parámetros en forma de array. Además, también controlaremos que no haya errores en la comunicación.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
    require_once "nusoap.php";
    $cliente = new nusoap_client("http://localhost/producto.php");
      
    $error = $cliente->getError();
    if ($error) {
        echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
    }
      
    $result = $cliente->call("getProd", array("categoria" => "libros"));
      
    if ($cliente->fault) {
        echo "<h2>Fault</h2><pre>";
        print_r($result);
        echo "</pre>";
    }
    else {
        $error = $cliente->getError();
        if ($error) {
            echo "<h2>Error</h2><pre>" . $error . "</pre>";
        }
        else {
            echo "<h2>Libros</h2><pre>";
            echo $result;
            echo "</pre>";
        }
    }
?>

Con esto ya tienes una idea múy básica del funcionamiento de un webservice SOAP construído con PHP. Pero claro, nos falta un archivo WSDL para tener un webservice decente. Aunque dicho archivo puede ser escrito a mano, NuSOAP puede generarlo por ti pasándole ciertos parámetros, por lo que lo ideal sería generarlo en el servidor. Así que modifica tu producto.php para que quede tal que así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
    require_once "nusoap.php";
      
    function getProd($categoria) {
        if ($categoria == "libros") {
            return join(",", array(
                "El señor de los anillos",
                "Los límites de la Fundación",
                "The Rails Way"));
        }
        else {
            return "No hay productos de esta categoria";
        }
    }
      
    $server = new soap_server();
    $server->configureWSDL("producto", "urn:producto");
      
    $server->register("getProd",
        array("categoria" => "xsd:string"),
        array("return" => "xsd:string"),
        "urn:producto",
        "urn:producto#getProd",
        "rpc",
        "encoded",
        "Nos da una lista de productos de cada categoría");
      
    $server->service($HTTP_RAW_POST_DATA);
?>

Como ves, el cambio en es cuando llamamos a register, ya que en vez de pasarle, como antes, el método en cuestión, le añadimos también varios argumentos para generar el WSDL:
  • El primer array nos permite definir el argumento de entrada y su tipo de datos
  • El segundo define la función de retorno y su tipo de datos
  • urn:producto es la definición del namespace
  • urn:producto#getProd es donde definimos la acción SOAP
  • Luego viene el tipo de llamada,que puede ser rpc, como en el ejemplo, o document
  • Tras esto definimos el valor del atribute use, que puede ser encoded o literal
  • Finalmente viene una descripción de qué hace el método al que llamamos
Ahora basta con que en el navegador accedas a producto.php?wsdl y verás el WSDL generado. Ya puedes copiarlo y añadirlo a tu directorio web (crea un archivo y llámalo, por ejemplo, libros.wsdl). Para que el cliente lo utilice debes modificar el código, y en el constructor, en vez del url le pasas el nombre del archivo, de forma que quede como en el ejemplo:

1
$client = new nusoap_client("libros.wsdl", true);

Ahora sí, ya tienes montado un pequeño servicio web. El ejemplo es simplón, pero piensa en todas las funcionalidades que podrías incorporarle.

Cómo realizar peticiones AJAX cross-domain usando JSONP y jQuery

Si ya os habéis familiarizado con la idea de realizar peticiones AJAX en jQuery, hoy vamos a darle una vuelta de tuerca y vamos a ver cómo realizar peticiones contra otros dominios que no son el nuestro.

Por motivos de seguridad, las peticiones por defecto en jQuery al usar AJAX, no permiten que sea a otros dominios. No obstante, a veces resulta útil o incluso necesario, realizar peticiones entre dominios para hacer uso de APIs externas o usar nuestras propias APIs desde local o desde cualquier sitio.

Por suerte, AJAX permite hacer peticiones cross-domain para obtener contenido JavaScript. Para ello necesitamos hacer uso de JSONP que, a un nivel muy básico, lo que hace es que en vez de pasar JSON plano, lo pasamos dentro de una función de JavaScript.

Hacerlo es rápido y sencillo pero necesita ajustes tanto en la parte cliente como en la parte servidor.

Cliente

$.ajax({ url : 'http://www.aqui-la-url.com', dataType : 'jsonp', data : data });

¿Y ya está? Sí. Tan simple como eso. Lo que hacemos es incluir en el dataType el valor jsonp, en vez de json, para indicar que estamos realizando una petición cross-domain y que esperamos este tipo de contenido. La única parte “difícil” es la parte del servidor. Únicamente voy a ponerla en PHP pero se podría adaptar a cualquier lenguaje.

Servidor

<?php  
$array = array("mensaje" => "Hola desde otro punto de la red"); //Por ejemplo
if(isset($_GET['callback'])){ // Si es una petición cross-domain  
  echo $_GET['callback'].'('.json_encode($array).')';
}
else // Si es una normal, respondemos de forma normal  
  echo json_encode($array);
?>

Voy a explicar un poco. Cuando hacemos una petición JSONP, jQuery añade un identificador único a cada petición y va en forma de atributo callback. En la imagen que acompaña estas líneas, podéis ver una petición que realiza BuSeViCi al pedir tiempos de una línea de Autobuses. Como podeis observar, el valor de la variable callback es jQuery17107112555727362633_1334050373252.

En el lado del servidor comprobamos que hemos recibido esta variable, ya que únicamente se manda (a no ser que especifiquemos otra cosa), si vamos a hacer este tipo de peticiones. Para que el navegador sea capaz de entender la petición de vuelta, el servidor debe devolver el resultado, encapsulado en esa función. Para que también podamos usar peticiones normales desde nuestro servidor, devolvemos el json normal en caso de que la petición no contenga la variable callback.

La respuesta devuelta en el caso de la petición de la imagen, es:

jQuery17107112555727362633_1334050373252({"12":null});  

Conclusión

Como ves, es muy sencillo hacer peticiones cross-domain y es un requisitio imprescindible para atacar a APIs como la de Twitter.

Si el artículo te pareció interesante, útil o incluso equivocado, por favor considera el dejar un comentario. ¡Lo apreciaré mucho!