Sobre la normativa de uso de cookies en sitios web

A raíz de la aparición de artículos de abogados haciendo referencia a las primeras sanciones por el uso “incorrecto de cookies”, concretamente este, he decidido publicar por aquí mi opinión sobre el tema.

Contexto

Hace relativamente poco tiempo, se aprobó una reforma por la cual se obligaba a los sitios web que instalasen cookies a notificar al usuario y pedir confirmación de que el mismo deseaba instalarlas en su navegador antes de que el sitio web lo hiciese.

La AEPD publicó una guía para explicar en detalle cómo y por qué se debe avisar al usuario de que el sitio web almacena información en su navegador.

Problema

Podemos decir que todos los sitios y aplicaciones web en Internet a los cuales se accede desde un navegador necesitan usar cookies. Hay excepciones, pero no pertenecen a lo que podemos calificar como sitio web o aplicación web normal.

Algunas cookies permiten que el usuario pueda iniciar sesión, otras recordarle aunque cierre el navegador para que no tenga que volver a indicar su usuario y contraseña, otras ayudan a mejorar la usabilidad y posicionamiento de la página, otras ayudan a mejorar la precisión a la hora de mostrar anuncios al usuario y otras tienen fines que podemos calificar de menos éticos.

Hasta ahora, el uso de las cookies se especificaba en los términos y condiciones del sitio web pero tras la reforma ya esto deja de ser suficiente y tendremos que avisar al usuario de que vamos a instalar cookies — antes de hacerlo — que hacen funcionar cosas tan normales como Google Analytics, videos de Youtube o el ya típico login  de Facebook.

El problema reside en que un aviso similar al que se pide puede echar atrás a una persona que no está entrenada o no entienda muy bien “los riesgos” que conlleva dejar al sitio web instalar las cookies.

Estamos viendo noticias de cómo Gobiernos espían a sus usuarios, cómo redes sociales hacen data mining con los datos de sus usuarios para venderlos a marcas o agencias de marketing, cómo se dan casos de estafa en sitios web y, aunque estas cosas tengan poco o nada que ver con el tema que estamos tratando, los usuarios ya tienen una desconfianza adquirida por ese contexto.

Esa desconfianza también es adquirida gracias a sitios web que permiten ver contenido online como series o películas, en las que al usuario se le bombardea con decenas de avisos en los que debe “aceptar o cancelar” y, dependiendo si se equivoca o no, puede acabar en un anuncio de contenido bien distinto a lo que buscaba.

Personalmente creo que el contexto no es favorable para añadir avisos o notificaciones que alerten al usuario de que vamos a hacer algo que llevamos haciendo desde que existe Internet y no tiene por qué suponer un riesgo a esa persona o su navegador.

Por otro lado, el rechazar el uso de cookies puede generar una mala experiencia de usuario que perjudique directamente al negocio y ventas.

Ni decir tiene que si un sitio instala cookies para mostrar publicidad al usuario y es su modelo de negocio principal, el que el usuario rechace las mismas puede suponer un problema muy serio para dicha empresa.

Una solución mejor

Aunque creo que es labor del usuario conocer de otra forma que existe un contrato con ese servicio y que en ese contrato se especifique cómo gestionar las cookies referentes a ese sitio web, pienso que una solución mejor sería que fuesen los navegadores los que se encargasen de la tarea de avisar al usuario por defecto, sin tener que configurar nada en los mismos.

De esta forma los usuarios aprenderían a reconocer y diferenciar este tipo de avisos, de otros como banners o advertencias que aparezcan en el propio sitio web.

Actualmente recae en el sitio web el poner la advertencia, y eso puede influir a que cada uno use un diseño y textos distintos, en idiomas distintos. Algo que algunos sitios web podrían usar en contra del usuario, mostrando avisos similares para confundirle y terminar llevándole a una página publicitaria o algo más ofensivo.

Cómo configurar el “remember me” en Silex

Hoy he implementado la funcionalidad de remember me en una aplicación Silex haciendo uso de los proveedores SecurityServiceProvider junto al RememberMeServiceProvider.

Al registrar el RememberMeServiceProvider me daba la siguiente excepción:

InvalidArgumentException: Identifier “security.remember_me.service.{key del firewall}” is not defined.

El problema residía en que registraba el RememberMeServiceProvider antes del SecurityServiceProvider, y eso era lo que lanzaba esa excepción.

Por lo tanto, para que funcione sin problemas deberíamos tener una configuración como la siguiente, respetando como he dicho el orden a la hora de registrar los proveedores de servicios:


$app->register(new Silex\Provider\SessionServiceProvider());


$app->register(new Silex\Provider\SecurityServiceProvider(), array(
    'security.firewalls'    => array(
        'site'  => array(
               
               'remember_me'   => array(),
               'pattern'       => '^/',
               'anonymous'     => true,
               'form'          => array(
                          'login_path' => 'login',
                          'check_path' => 'login_check'
                ),
                'users' => $app->share(function () use ($app) {
                    return new \Lib\UserProvider($app);
                }),
                'logout' => array('logout_path' => 'logout'),
         ),
    ),
    'security.access_rules' => array(
         array('^/login', 'IS_AUTHENTICATED_ANONYMOUSLY')
    )
));

$app->register(new \Silex\Provider\RememberMeServiceProvider());

Silex con Doctrine DBAL y ORM

En este artículo explicaré cómo utilizar Doctrine y sus entidades en el microframework Silex.

Aunque no es obligatorio, os recomiendo que utilicéis el “esqueleto” para Silex que ha creado Fabien Potencier para estructurar los archivos, o al menos le echéis un ojo para tenerla como ejemplo.

Para tener una integración completa de Doctrine en Silex, necesitaréis los siguientes providers y librerías:

  • DoctrineServiceProvider
    Es un proveedor de servicios oficial, que nos permitirá utilizar DBAL, la capa que nos da Doctrine para trabajar de forma sencilla con PDO.
  • Doctrine ORM ServiceProvider de Dragonfly Development
    Este proveedor nos permitirá trabajar con Doctrine ORM y sus entidades.
  • DoctrineBridge
    Con DoctrineBridge tendremos las librerías necesarias para poder loguear nuestras consultas sin tener que hacer un desarrollo propio para ello.

Los pasos para tener todo funcionando son los siguientes:

Configurar las dependencias en el composer.json

Debemos añadir los siguientes requires para descargar las dependencias anteriores


"doctrine/dbal": "2.*",
"symfony/doctrine-bridge": "2.*",
"dflydev/doctrine-orm-service-provider": "1.0.*@dev"

Una vez añadidas las anteriores líneas a nuestros requires, actualizamos con composer:

php composer.phar update

Registrar los ServiceProvider

Si utilizamos el skeleton de Fabien, los incluiremos en el archivo app.php, en caso contrario, los añadiremos en el lugar donde estemos registrando el resto de los ServiceProviders.


$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
    'db.options'    => array(
    'driver'        => 'pdo_mysql',
    'host'          => 'localhost',
    'dbname'        => 'name',
    'user'          => 'root',
    'password'      => '',
    'charset'       => 'utf8',
    'driverOptions' => array(1002 => 'SET NAMES utf8',),
  ),
));

$app->register(new Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider, array(
    "orm.em.options" => array(
         "mappings" => array(
            array(
               "type"      => "yml",
               "namespace" => "Entity",
               "path"      => realpath(__DIR__."/../config/doctrine"),
              ),
            ),
         ),
));

Como vemos, primero registramos el Provider oficial, en el que indicamos los datos de conexión. Si dispusiésemos de varios servidores de bases de datos, para dividir las escrituras y las lecturas por ejemplo, este sería el lugar para hacerlo.

A continuación, registramos el proveedor de dflydev para disponer del ORM. Es importante fijarse en los parámetros

  • type: Define qué tipo de mapeo queremos para las entidades de doctrine. Las posibilidades son annotations, yml o xml.
  • namespace: Indica el namespace para las clases del ORM.
  • path: Indica en qué carpeta tenemos la configuración de las entidades.

Yo he optado por utilizar un mapeo basado en Yaml y guardo los mapeos en la carpeta /config/doctrine. Las clases de las entidades estarán bajo el namespace Entity.

Añadir los comandos de Doctrine

Para disponer de los comandos que nos permitirán generar las entidades, vamos al archive console.php entro del directorio src si utilizamos el skeleton, en caso contrario vamos al archivo donde tengamos la configuración de la consola y añadimos lo siguiente:


$console->setHelperSet(new Symfony\Component\Console\Helper\HelperSet(array(
    'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($app["db"]),
    'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($app["orm.em"])
)));

$console->addCommands(array(

  new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand,
  new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand,
  new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand,
  new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand,
  new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand,
  new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand,
  new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand,
  new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand,
  new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand,
  new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand,
  new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand,
  new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand,
  new \Doctrine\ORM\Tools\Console\Command\InfoCommand,
  new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand,
  new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand,
  new \Doctrine\DBAL\Tools\Console\Command\ImportCommand,
  new \Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand,
  new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand

));

Esto registrará los comandos y helpers que nos da doctrine para gestionar las entidades y el esquema de base de datos.

Uso en la aplicación

Tras estos pasos sólo nos queda usarlo. Para ello creamos el mapa de entidad Entity.User.dcm.yml en el directorio que hayamos indicado en la configuración del proveedor:

#config/doctrine/Entity.User.dcm.yml
Entity\User:
    type: entity
    table: user
    fields:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
        email:
            type: string
            length: 255
        password:
            type: string
            length: 512
    lifecycleCallbacks: {  }

Ahora, mediante la línea de comandos ejecutamos lo siguiente.

php console orm:generate-entities src/ --generate-annotations=1

Esto nos creará la carpeta Entity/ en el directorio src/ con las clases de las entidades, en este caso la clase Entity\User.

Para crear la base de datos, utilizaríamos el comando

php console orm:schema-tool:create

Esto nos creará la base de datos en el servidor. Ahora ya sólo nos queda el realizar operaciones con nuestros usuarios.

$app->get("/test-users", function(Application $app){

  $user = new \Entity\User();
  $user->setEmail("example@simettric.com");
  $user->setPassword("lalala");

  $entity_manager = $app["orm.em"];

  $entity_manager->persist($user);
  $entity_manager->flush();

  $users = $entity_manager->getRepository("Entity\User")
                          ->findAll();

  return $app['twig']->render('users.html.twig',
                              array("users"=>$users));

});

Seguridad.

Si utilizamos el SecurityProvider, seguro que unos interesa realizar la autenticación con nuestros usuarios en la base de datos utilizando Doctrine.

Para ello lo primero que debemos hacer es crearnos un UserProvider.


namespace Lib\Provider;

use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

class UserProvider implements UserProviderInterface
{
    private $app;

    public function __construct(\Silex\Application $app)
    {
        $this->app = $app;
    }

    public function loadUserByUsername($username){


        $em = $this->app["orm.em"];
        if($em instanceof \Doctrine\ORM\EntityManager){
            if(!$user = $em->getRepository("Entity\User")->findOneBy(array("username"=>$username))){
                throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
            }
        }

        return $user;

    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof \Entity\User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        return $this->loadUserByUsername($user->getUsername());
    }


    public function supportsClass($class)
    {
        return $class === '\Entity\User';
    }



}

Y en nuestra configuración de seguridad, añadir lo siguiente

$app->register(new Silex\Provider\SecurityServiceProvider(), array(
    'security.firewalls'    => array(
        'dev' => array('pattern' => '^/(_(profiler|wdt)|css|images|js)/', 
                       'security' => false),
        'site' => array(
            'pattern'     => '^/',
            'form'        => array('login_path' => 'login',
                                   'check_path' => 'login_check'),
            'users'       => $app->share(function () use ($app) {
                return new \Lib\Provider\UserProvider($app);
            }),
            'logout' => array('logout_path' => 'logout'),
        ),
    ),
    'security.encoders'  => array('Entity\User'=> array(
                                     'algorithm'        => 'sha1', 
                                     'iterations'       => 4,         
                                     'encode_as_base64' => false
))));

Con esto ya tenemos nuestro sistema de seguridad autenticando con nuestros usuarios de la base de datos, utilizando Doctrine.

Symfony2 Security UsernamePasswordToken::serialize() must return a string or NULL

Today I got the following exception in a web application that uses Doctrine2 Entities and the Symfony Security Component with Silex framework (this can also appear in Symfony2 framework).

Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::serialize() must return a string or NULL

ErrorException: Notice: serialize(): "id" returned as member variable from __sleep() but does not exist in /../vendor/symfony/security/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php line 149

The error appears when you have a Doctrine Entity implementing the UserInterface with a relation mapped in it.

The solution is as simple as implement the __sleep() magic method in that entity in this way.


public function __sleep(){
   return array('id', 'username', 'email');
}

This sets the id, email and username as the desired attributes to serialize ignoring the mappings and other properties in the Entity.

Conceptos sobre APIs REST

En esta entrada voy a resumir los conceptos más importantes que he tratado en mis ponencias sobre REST.

¿Qué es REST?

REST, REpresentational State Transfer, es un tipo de arquitectura de desarrollo web que se apoya totalmente en el estándar HTTP.

REST nos permite crear servicios y aplicaciones que pueden ser usadas por cualquier dispositivo o cliente que entienda HTTP, por lo que es increíblemente más simple y convencional que otras alternativas que se han usado en los últimos diez años como SOAP y XML-RPC.

REST se definió en el 2000 por Roy Fielding, coautor principal también de la especificación HTTP. Podríamos considerar REST como un framework para construir aplicaciones web respetando HTTP.

Por lo tanto REST es el tipo de arquitectura más natural y estándar para crear APIs para servicios orientados a Internet.

Existen tres niveles de calidad a la hora de aplicar REST en el desarrollo de una aplicación web y más concretamente una API que se recogen en un modelo llamado Richardson Maturity Model en honor al tipo que lo estableció, Leonard Richardson padre de la arquitectura orientada a recursos. Estos niveles son:

  1. Uso correcto de URIs
  2. Uso correcto de HTTP.
  3. Implementar Hypermedia.

Además de estas tres reglas, nunca se debe guardar estado en el servidor, toda la información que se requiere para mostrar la información que se solicita debe estar en la consulta por parte del cliente.

Al no guardar estado, REST nos da mucho juego, ya que podemos escalar mejor sin tener que preocuparnos de temas como el almacenamiento de variables de sesión e incluso, podemos jugar con distintas tecnologías para servir determinadas partes o recursos de una misma API.

Nivel 1: Uso correcto de URIs

Cuando desarrollamos una web o una aplicación web, las URLs nos permiten acceder a cada uno de las páginas, secciones o documentos del sitio web.

Cada página, información en una sección, archivo, cuando hablamos de REST, los nombramos como recursos.

El recurso por lo tanto es la información a la que queremos acceder o que queremos modificar o borrar, independientemente de su formato.

Las URL, Uniform Resource Locator , son un tipo de URI, Uniform Resource Identifier, que además de permitir identificar de forma única el recurso, nos permite localizarlo para poder acceder a él o compartir su ubicación.

Una URL se estructura de la siguiente forma:

{protocolo}://{dominio o hostname}[:puerto (opcional)]/{ruta del recurso}?{consulta de filtrado}

Por ejemplo, http://asiermarques.com/2013/conceptos-sobre-apis-rest/, sería la URL para visualizar este artículo.

Existen varias reglas básicas para ponerle nombre a la URI de un recurso:

  • Los nombres de URI no deben implicar una acción, por lo tanto debe evitarse usar verbos en ellos.
  • Deben ser únicas, no debemos tener más de una URI para identificar un mismo recurso.
  • Deben ser independiente de formato.
  • Deben mantener una jerarquía lógica.
  • Los filtrados de información de un recurso no se hacen en la URI.

Las URIs no deben implicar acciones y deben ser únicas

Por ejemplo, la URI /facturas/234/editar sería incorrecta ya que tenemos el verbo editar en la misma.

Para el recurso factura con el identificador 234, la siguiente URI sería la correcta, independientemente de que vayamos a editarla, borrarla, consultarla o leer sólo uno de de sus conceptos: /facturas/234

Las URIs deben ser independientes de formato

Por ejemplo, la URI /facturas/234.pdf no sería una URI correcta, ya que estamos indicando la extensión pdf en la misma.

Para el recurso factura con el identificador 234, la siguiente URI sería la correcta, independientemente de que vayamos a consultarla en formato pdf, epub, txt, xml o json: /facturas/234

Las URIs deben mantener una jerarquía lógica

Por ejemplo, la URI /facturas/234/cliente/007 no sería una URI correcta, ya que no sigue una jerarquía lógica.

Para el recurso factura con el identificador 234 del cliente 007, la siguiente URI sería la correcta: /clientes/007/facturas/234

Filtrados y otras operaciones.

Para filtrar, ordenar, paginar o buscar información en un recurso, debemos hacer una consulta sobre la URI, utilizando parámetros HTTP en lugar de incluirlos en la misma.

Por ejemplo, la URI /facturas/orden/desc/fecha-desde/2007/pagina/2 sería incorrecta ya que el recurso de listado de facturas sería el mismo pero utilizaríamos una URI distinta para filtrarlo, ordenarlo o paginarlo.

La URI correcta en este caso sería:

/facturas?fecha-desde=2007&orden=DESC&pagina=2

Nivel 2: HTTP

Conocer bien HTTP no es opcional para un desarrollador web al que le importe su trabajo. Aunque el RFC es sencillo de leer, si estás interesado en aprender bien las bases de este protocolo es muy recomendable la guía de O’Reilly sobre el mismo.

Para desarrollar APIs REST los aspectos claves que hay que dominar y tener claros son:

  • Métodos HTTP
  • Códigos de estado
  • Aceptación de tipos de contenido

Métodos.

Como hemos visto en el anterior nivel, a la hora de crear URIs no debemos poner verbos que impliquen acción, aunque queramos manipular el recurso.

Para manipular los recursos, HTTP nos dota de los siguientes métodos con los cuales debemos operar:

  • GET: Para consultar y leer recursos
  • POST: Para crear recursos
  • PUT: Para editar recursos
  • DELETE: Para eliminar recursos.
  • PATCH: Para editar partes concretas de un recurso.

Por ejemplo para un recurso de facturas.

GET /facturas Nos permite acceder al listado de facturas

POST /facturas Nos permite crear una factura nueva

GET /facturas/123 Nos permite acceder al detalle de una factura

PUT /facturas/123 Nos permite editar la factura, sustituyendo la totalidad de la información anterior por la nueva.

DELETE /facturas/123 Nos permite eliminar la factura

PATCH /facturas/123 Nos permite modificar cierta información de la factura, como el número o la fecha de la misma.

Quizá debido al desconocimiento o el soporte de ciertos navegadores, los desarrolladores web han usado, durante los últimos años, únicamente los métodos GET Y POST para realizar todas estas acciones. Si trabajamos con REST, esto sería un error de base y puede darnos problemas incluso a la hora de nombrar nuestros recursos, obligándonos a poner verbos en las URLs.

Códigos de estado.

Uno de los errores más frecuentes a la hora de construir una API suele ser el reinventar la rueda creando nuestras propias herramientas en lugar de utilizar las que ya han sido creadas, pensadas y testadas. La rueda más reinventada en el desarrollo de APIs son los códigos de error y códigos de estado.

Cuando realizamos una operación, es vitar saber si dicha operación se ha realizado con éxito o en caso contrario, por qué ha fallado.

Un error común sería por ejemplo:

Petición
========
PUT /facturas/123

Respuesta
=========
Status Code 200
Content:
{
  success: false,
  code:    734,
  error:   "datos insuficientes"
}

En este ejemplo se devuelve un código de estado 200, que significa que la petición se ha realizado correctamente, sin embargo, estamos devolviendo en el cuerpo de la respuesta un error y no el recurso solicitado en la URL.

Este es un error común que tiene varios inconvenientes:

  • No es REST ni es estándar.
  • El cliente que acceda a este API debe conocer el funcionamiento especial y cómo tratar los errores de la misma, por lo que requiere un esfuerzo adicional importante para trabajar con nosotros.
  • Tenemos que preocuparnos por mantener nuestros propios códigos o mensajes de error, con todo lo que eso supone.

HTTP tiene un abanico muy amplio que cubre todas las posibles indicaciones que vamos a tener que añadir en nuestras respuestas cuando las operaciones han ido bien o mal.

Es imperativo conocerlos y saber cuándo utilizarlos, independientemente de que desarrolles siguiendo REST.

El siguiente ejemplo sería correcto de la siguiente forma:

Petición
========
PUT /facturas/123

Respuesta
=========
Status Code 400
Content:
{
  message: "se debe especificar un id de cliente para la factura"
}

Tipos y formatos de contenido.

Cuando hablamos sobre URLs, vimos también que no era correcto indicar el tipo de formato de un recurso al cual queremos acceder o manipular.

HTTP nos permite especificar en qué formato queremos recibir el recurso, pudiendo indicar varios en orden de preferencia, para ello utilizamos el header Accept.

Nuestra API devolverá el recurso en el primer formato disponible y, de no poder mostrar el recurso en ninguno de los formatos indicados por el cliente mediante el header Accept, devolverá el código de estado HTTP 406.

En la respuesta, se devolverá el header Content-Type, para que el cliente sepa qué formato se devuelve, por ejemplo:

Petición
========
GET /facturas/123
Accept: application/epub+zip , application/pdf, application/json

Respuesta
=========
Status Code 200
Content-Type: application/pdf

En este caso, el cliente solicita la factura en epub comprimido con ZIP y de no tenerlo, en pdf o json por orden de preferencia. El servidor le devuelve finalmente la factura en pdf.

Nivel 3: Hypermedia.

A pesar de lo que nos pueda inducir a pensar el término retrofuturista Hypermedia, el concepto y la finalidad que busca describir es bastante sencillo: conectar mediante vínculos las aplicaciones clientes con las APIs, permitiendo a dichos clientes despreocuparse por conocer de antemano del cómo acceder a los recursos.

Con Hypermedia básicamente añadimos información extra al recurso sobre su conexión a otros recursos relacionados con él.

Aquí tenemos un ejemplo:

<pedido>
 <id>666</id>
 <estado>Procesado</estado>
 <links>
   <link rel="factura">
       http://example.com/api/pedido/666/factura
   </link>
 </links>
</pedido>

En este ejemplo vemos cómo indicar en un xml que representa un pedido, el enlace al recurso de la factura relacionada con el mismo.

Sin embargo, necesitamos que el cliente que accede a nuestra API entienda que esa información no es propia del recurso, sino que es información añadida que puede utilizar para enlazar el pedido con la factura.

Para ello conseguir esto, debemos utilizar las cabeceras Accept y Content-Type, para que tanto el cliente como la API, sepan que están hablando hypermedia.

Por ejemplo:

Petición
========
GET /pedido/666
Accept: application/nuestra_api+xml, text/xml

Respuesta
=========
Status Code: 200
Content-Type: application/nuestra_api+xml
Content:

<pedido>
 <id>666</id>
 <estado>Procesado</estado>
 <links>
   <link rel="factura">
        http://example.com/api/pedido/666/factura
   </link>
 </links>
</pedido>

Como vemos, el cliente solicita el formato application/nuestra_api+xml de forma preferente al formato text/xml. De esta forma, le indica al servicio web, que entiende su formato hypermedia y puede aprovecharlo.

El servicio web por lo tanto, como implementa hypermedia, le devuelve la información de recurso y la información de hypermedia que puede utilizar el cliente.

Hypermedia es útil por ejemplo para que el cliente no tenga que conocer las URLs de los recursos, evitando tener que hacer mantenimientos en cada uno de los mismos si en un futuro dichas URLs cambian (cosa que no debería pasar). También es útil para automatizar procesos entre APIs sin que haya interacción humana.

Conclusión

Como hemos visto, los principios básicos para construir APIs REST se basan en conocer sobre todo HTTP, algo que no es opcional para un desarrollador web.

Versiones en nuestra API REST

Una de las cosas que hay que tener en cuenta cuando hacemos una API es que esta, al igual que otro tipo de aplicaciones web, va a cambiar.

Si la API es pública o es consumida por una gran cantidad de clientes, es casi seguro que muchos de estos clientes queden obsoletos o no sigan al día las actualizaciones que tu equipo vaya haciendo en la misma.

La solución consiste en dar compatibilidad a las versiones anteriores y de alguna forma separar o que el cliente pueda indicar a qué versión está accediendo en cada momento.

Las opciones más habituales para hacer esto serían:

Indicar la versión de la API en la URL del recurso.

https://api.dominio.com/v1/recurso/

Este sería un ejemplo de URL en la que se indica la versión de la API en la misma.

Esta es la opción más aconsejada pero la que a mí personalmente menos me convence ya que los clientes que quisieran estar actualizados a la última versión, deberían modificar las rutas de sus llamadas al servicio constantemente.

Indicar como parámetro la versión de la API

https://api.dominio.com/recurso/?v=1

Este tipo de opción se usa por ejemplo en la API de YouTube.

Yo personalmente prefiero indicar como parámetros opciones para filtrar o tratar la información del recurso solicitado. Por ejemplo, paginaciones, órdenes, búsquedas, especificar información parcial etc.

Indicar como header la versión de la API

https://api.dominio.com/recurso/ 
“API version”: 1

Esta opción es la que más me convence, ya que no se ensucia la URL y si la aplicación cliente tiene las llamadas centralizadas en un único punto no debería ser problema añadir un header extra indicando la versión del API.

Después en el servidor podremos separar las versiones de las APIs en distintos servidores web o aplicaciones mediante un servidor proxy HTTP como Varnish que lea el header del request y sirva la versión de la API solicitada de forma transparente.

En javascript por ejemplo tendríamos el siguiente código para añadir un header http a nuestra llamada AJAX.


var request = new XMLHttpRequest();
request.open("GET", "https://api.dominio.com/recurso/", false);
request.setRequestHeader("API version", "1");
request.send();

En jQuery se añadiría el header de la siguiente forma.


$.ajax(
  "https://api.dominio.com/recurso/",
  {"headers": {"API version": "1"}}
);

En cualquier caso, se elija la opción que se elija es importante tener en cuenta que nuestra API puede cambiar en el futuro y facilitar la vida a los desarrolladores que programen los clientes de la misma siempre es una buena práctica.

Una reflexión sobre Symfony2 y sus componentes

Me ha alegrado leer la noticia que resume la reunión de ayer día 3 de Noviembre en symfony.es, proyecto que mantiene Javier Eguiluz, en la que dice que Fabien Potencier, autor y líder del proyecto Symfony, quiere dar prioridad a la documentación de los Componentes del framework.

Es una gran noticia que se considere documentar cada componente para final de año, y va muy en la línea de lo que comentaba Fabien en su blog sobre qué es Symfony2 y qué no.

Fabien quiere dar mucha importancia a los Componentes ya que es la única forma de que Symfony2 esté presente en el mayor número de proyectos posibles.

En mi opinión, y como ya he expresado en otras ocasiones, Symfony2 es un framework enorme, que intenta solucionar demasiada problemática, y en mi experiencia, cuanto más grande sea la aplicación y cuanta más problemática se intente cubrir, más complejo después será encontrar un posible fallo en entornos que no se hayan tenido en cuenta para la misma. En aplicaciones orientadas a internet, el entorno de sistemas puede que no sea el estándar LAMP, sobre todo si nos enfrentamos a webs de alto tráfico.

Otro problema, que para otros es una virtud, es que el framework intenta hacer configurable todo, con el objetivo de que los proyectos sean lo más automatizables, mantenibles y dotarles de una capacidad de reutilización en la que llegue a ser innecesario tocar una línea de código. Esto en teoría, suma más horas de desarrollo al principio y luego hace el mantenimiento más barato. Sin embargo, en proyectos que se orientan a internet, la teoría queda muchas veces en evidencia.

Hay que tener en cuenta que el mundo php5 no es como el mundo .net o java. Es complicado que desarrolladores web de php, aun con experiencia, se conviertan tan puristas de repente (más teniendo en cuenta los presupuestos tan ajustados económicamente y en tiempo que se barajan en proyectos web para internet) como para adoptar y entender todas las buenas prácticas que te obliga a usar Symfony2.

Para la mayoría de desarrolladores, el disponer de componentes, no les obliga a comprender el funcionamiento de todo el framework y pueden usarlos en desarrollos más simples o que ya tengan hechos.

De hecho, viendo estas direcciones en el proyecto Symfony2, en mi propio proyecto Leophard, un framework mvc ligero para php que por diversas razones de peso he tenido que retrasar su desarrollo, usaré algunos de estos componentes como el EventDispatcherUniversalClassLoader, el Routing, Pimple o el HttpFoundation, que son ligeros y resuelven problemática muy importante del proyecto.

La gente de Drupal está usando algunos de estos componentes también en la próxima versión de su cms.

Jugando con ips y redes en php

Por la naturaleza de mi entorno profesional, lidio con conceptos de IT y redes en mi día a día que me ayudan a tener frescos conceptos que aprendí hace ya algún tiempo cursando el temario del programa CCNA de Cisco y del MCSE de Microsoft.

Concretamente en el área de redes, es muy útil saber cómo hacer subredes o saber a qué red pertenece una ip en concreto haciendo operaciones AND binarias con las máscaras y direcciones a evaluar.

Refresco de conceptos

Las direcciones ip se clasifican en lo que llamamos “clases de red” según el número de hosts que dichas redes puedan soportar (siendo un host un ordenador, impresora o cualquier cacharro que conectemos a dicha red).

Usamos comúnmente las siguientes clases de red:

  • Clase C, la más común, que puede tener hasta 254 dispositivos host conectados.
  • Clase B, que puede tener hasta 65.534 dispositivos host conectados.
  • Clase A, que puede tener hasta 16.777.214 dispositivos host conectados.

Para saber de qué clase es una red o una dirección ip, usamos máscaras de red. Las máscaras de red se componen de 4 fracciones y en cada una podemos rellenar hasta 8bits.

En una dirección de clase C tendremos un total de 24bits, en binario podríamos expresarla entonces como 11111111.11111111.11111111.00000000 (24bits = 8bits + 8 bits +8bits + 0bits) y en decimal como 255.255.255.0 .

No necesitamos escribir la máscara completa para indicar que la ip 192.168.1.2 es de clase C por ejemplo, bastaría con indicar al final de la misma que su máscara tiene 24bits de la siguiente forma: 192.168.1.2/24 .

En el resto de clases, tendríamos 8bits para las redes de clase A y 16bits para las de clase B.

Sin embargo, aunque en entornos domésticos o de mediana empresa se usen estas clases de red para crear las redes a las cuales se conecten los ordenadores, servidores e impresoras de la organización, en una empresa que provee servicios de internet o que tiene muchos departamentos, le es muy útil poder partir esas redes en subredes.

Hay escenarios en los que nos interesa tener varias subredes con un número concreto de equipos en cada una de ellas. De esta forma aislamos a qué recursos puede acceder uno o varios equipos de una determinada subred.

Para hacer subredes lo que se suele hacer es usar máscaras de red personalizadas. Por ejemplo, si tenemos la ip 192.168.129.3/18, tal y como hemos visto antes sabemos que su máscara de red tiene 18bits, es decir en binario 11111111.11111111.11000000.00000000 y en decimal 255.255.192.0. Ahora bien, al no tener una máscara de 16bits o de 24, ya que tenemos una fracción de las 4 sin rellenar totalmente por bits en la máscara de subred, no podríamos decir que esa ip pertenece a la red 192.168.129.0/24, ya que esta sería una dirección de red de clase C y la nuestra no pertenece a esa clase.

Para saber a qué dirección de red pertenece tenemos que hacer una operación AND a nivel de bits binarios entre la dirección ip y su máscara de red.

En este caso la operación quedaría así:

Operación con ips

Al final deducimos que la dirección de red a la que pertenece la ip 192.168.129.3 es 192.128.0.0.

También podemos realizar operaciones para saber la cantidad de dispositivos que se pueden conectar a una determinada red o calcular una red que pueda albergar un determinado número de equipos.

Para calcular el número de dispositivos usamos la función 2x -2 donde X es la cantidad de bits disponibles en una subred es decir, el número de ceros que quedan al final de la misma. En la máscara de 18 bits que hemos usado en el ejemplo (11111111.11111111.11000000.00000000) serían 14 los bits disponibles (32bits posibles totales – 18 que estamos usando).

Aplicando la fórmula nos daría un total de 16.382 cacharros que podríamos conectar a nuestra red.

Utilizando php para realizar estos cálculos.

En php disponemos de una serie de funciones y operadores que nos hacen muy sencillo realizar cálculos de este tipo y automatizar este trabajo que de hacerlo manual nos haría consumir tiempo y estaría propenso a errores.

En este escenario utilizaremos tan sólo dos cosas:

  • La función ip2long para pasar una dirección ip separada por puntos a su representación numérica para poder realizar operaciones posteriores con ella.
  • La función decbin, para pasar de decimal a binario.
  • El operador bitwise & que es el que realizará la operación AND a nivel binario.

Volviendo al anterior ejemplo, podemos saber a qué dirección de red pertenece la ip 192.168.129.3/18 de la siguiente forma:


$ip_en_binario        = decbin( ip2long( "192.168.129.3" ) );

$mascara_en_binario   = decbin( ip2long( "255.255.192.0" ) );

$resultado_en_binario = $ip_en_binario & $mascara_en_binario;

echo long2ip( bindec( $resultado_en_binario ) );

Tal y como esperábamos, el resultado que nos devuelve este script es 192.168.128.0, que es la dirección de red a la que pertenece la ip 192.168.129.3/18.

Con este sencillo ejemplo vemos el juego que le podemos sacar a todo esto.

Por un lado se ve de forma clara que si queremos saber a qué red pertenece una ip dada de todas las que tenemos en nuestra empresa, las cuales almacenamos en una base de datos, bastaría con pasar a binario cada ip y compararla al resultado de la operación efectuada en el ejemplo.

Pero lo realmente útil es saber que podemos guardar en base de datos la dirección directamente en binario, ahorrando un espacio importante en la tabla al evitarnos usar un campo tipo texto (clave para el rendimiento con índices en motores como inndb de mysql) y realizar directamente la búsqueda contra el registro que necesitemos.

También podríamos conocer muy fácilmente con la formula que hemos visto antes, el número de equipos que puedo conectar a una red seleccionada y en base a las ips que podríamos tener registradas, los “huecos” e ips disponibles para los equipos nuevos que tenga que conectar a esa red en un futuro.

Symfony, wordpress o php desde 0

Este post es una reflexión sobre si realmente utilizar un framework es el camino para desarrollar aplicaciones web o es mejor utilizar un cms como WordPress o quizá una estructura de clases propia que reutilicemos en los futuros proyectos que afrontemos.

Utilizar un framework como Symfony

Para mí, adoptar Symfony como framework ha sido una de las mejores decisiones que he tomado en mi historia como desarrollador web.

Symfony es un framework excelente, muy personalizable, extendible y automatizado que además, te enseña a programar bien.

¿Qué es programar bien? Para empezar, usar patrones de diseño, aprovechar todas las ventajas que ofrece la POO al máximo posible ademas de, por encima de todo, evitar repetirte.

Symfony no es el único framework que permite esto, también Zend es otra opción excelente.

Sin embargo, en los últimos meses he tenido la oportunidad de participar en proyectos fuera de las necesidades comunes. Ya sea por rendimiento o por la naturaleza de la aplicación, teníamos que especializar bastante funcionalidad del core de symfony, aprender casi de memoria los parámetros de los archivos .yml de configuración o incluso, extender el framework para que cumpliese con los requisitos de la arquitectura de sistemas.

Esto supone en algunos casos muchas horas más que en un proyecto de php digamos “sin framework”, pero una vez conseguido el tiempo se reduce en la próxima ocasión que te encuentras con el mismo problema, si lo haces.

Hay pequeñas situaciones en las que debemos invertir tiempo para desarrollar pensando en el framework para obtener funcionalidad que sería trivial en un entorno sin framework. Sin embargo en general compensa porque nuestro código queda totalmente integrado con una arquitectura que tiene un nivel muy alto de calidad.

Pero otro problema aparece cuando de repente se anuncia una nueva versión del framework y empiezas a ver que todo lo que has aprendido en la versión 1, es completamente distinto en la versión 2. Esto es algo bastante costoso de asumir para los desarrolladores ya que algunas de las ventajas más importantes de utilizar un framework son precisamente las que en este caso, se ha puesto en contra de todos nosotros:

  1. Todos habíamos aprendido a programar en nuestro día a día pensando en symfony, con sus configuraciones y la nomenclatura de sus clases.
  2. Todos conocíamos perfectamente los nombres de los directorios, archivos de configuración y comportamientos de los métodos que se usan en las clases más importantes del framework, así como dónde teníamos que encontrar los recursos de documentación necesarios para nuestro día a día.
  3. Habíamos desarrollado código reutilizable como plugins, concepto que desaparece en la próxima versión.

Estos puntos son los más negros que quizá se puedan encontrar a la hora de decidirse por un framework que no sea uno nuestro propio. Sin embargo y a pesar de los mismos, creo que en general es una decisión muy buena el optar por un framework de desarrollo.

Utilizar un CMS como WordPress

Otra opción que existe es optar por un cms como WordPress. Hablo de WordPress porque es el que más conozco a “bajo nivel”. Si estás acostumbrado a utilizar un framework como Symfony, tienes la sensación al desarrollar bajo una plataforma para nada óptima (en cuanto a rendimiento se refiere) y que se salta a la torera cientos de recomendaciones sobre cómo desarrollar en un entorno POO.

Sin embargo el sistema funciona para la mayoría de proyectos medios o básicos, los diseñadores están encantados con él y ya lo conocen debido a la facilidad de uso de su sistema de themes, funciona sin tener que tocar prácticamente nada en la mayoría de plataformas de hosting y cubre con la necesidad funcional de todo sitio medio que no tenga necesidad de un desarrollo continuado.

Aunque sea una opción poco óptima fuera de la caja, en Blackslot tenemos corriendo varios sitios con decenas de miles de visitas al día basados en esta plataforma, con ayuda de soluciones de caché internas, teniendo aún margen para escalar a bastante más tráfico sin demandar grandes cantidades de recursos de servidor.

El problema reside más bien en el tipo de funcionalidad que quieres afrontar con el cms y la calidad que esperas obtener con el mismo. Yo no desarrollaría una intranet en WordPress por ejemplo, ni una plataforma de tienda electrónica para un negocio que dependa exclusivamente de esta plataforma para funcionar.

Utilizar PHP desde 0

Aunque durante muchos años he defendido el utilizar php sin frameworks hoy en día lo desaconsejo por completo.

Hace años no existía ninguna opción seria para php equiparable a un entorno .net por ejemplo. Hoy en día hay tantos que cuesta elegir.

No digo que debas elegir una de las opciones más maduras disponibles (que para mí sólo hay dos: Zend o Symfony). Lo que digo es que a menos que tu negocio sea una sola web y tengas el foco y todo tu tiempo puesta en ella, necesitas un framework para conseguir sobre todo estos puntos:

  1. Desarrollar con más programadores en el mismo proyecto sin que sea un caos.
  2. Trabajar con más gente en el mismo proyecto. A diferencia del punto 1, tienes que tener en cuenta a diseñadores, maquetadores, analistas, gente de sistemas..
  3. Tener una base reutilizable que os ahorre, a medida que desarrolléis con la misma, más y más tiempo.
  4. Conseguir automatizar las tareas más repetitivas y centrarte en el código específico de la aplicación que te ocupe en ese momento.

Aunque creas que lo mejor es ponerte a desarrollar un framework propio desde el principio, merece la pena conocer los frameworks que existen para aprender de miles de desarrolladores que han tenido muchos más problemas y por lo tanto tienen más experiencia que tú.

Desarrollar un framework sin haber utilizado otro antes como Symfony o asp.net mvc es una tarea muy compleja que puede hacerte perder mucho tiempo. Actualmente no es casualidad que Ruby On Rails, Symfony, asp.net mvc y otras opciones para desarrollo web escritas en diferentes lenguajes se parezcan entre sí. Esto es porque los problemas que nos solemos encontrar en el día a día son los mismos y es muy bueno conocer alguno de estos frameworks al detalle para orientarte sobre cómo debes organizar la arquitectura de una aplicación web seria.

En mi caso, incentivado por el cambio de versión de Symfony y por algunas de las cosas que no me han gustado o que he visto demasiado complejas en el mismo para solucionar determinados problemas, he empezado a diseñar y desarrollar en mis tiempos muertos un framework mucho más ligero y simple que Symfony pero pensado tanto para entornos tanto de hosting compartido como para arquitecturas más complejas.

Sin embargo no voy a dejar de utilizar ni Symfony ni WordPress. Me encanta Symfony y muchas de las cosas que he aprendido con este framework las estoy aplicando en este proyecto e incluso utilizo alguno de sus componentes. WordPress por su parte es la mejor opción para reducir costes en determinados proyectos que no requieran ni complejidad ni funcionalidad fuera de lo que se espera de un sistema de publicación.