jueves, 25 de julio de 2013

HTTP: diferencia entre POST y PUT

El protocolo HTTP define una serie de peticiones posibles entre cliente y servidor ( GET, POST, PUT, DELETE, HEAD, etc ). POST y PUT son muy similares y hay bastante confusión respecto a su uso y sus diferencias. Las dos pueden utilizarse para crear y para actualizar un recurso, pero en situaciones diferentes.

Hasta hace unos años se utilizaba casi exclusivamente GET y POST. La interacción con el servidor era básicamente por medio de formularios HTML, que no permiten otros métodos (ni siquiera en HTML5).

En las aplicaciones actuales, la mayor parte de la comunicación con el servidor se realiza mediante AJAX, que sí permite el uso de otras peticiones HTTP. Además, la popularización de los interfaces REST implica el uso de los métodos GET, POST, PUT y DELETE para interactuar con el servidor.

En algunas páginas sobre REST encontramos la siguiente definición de los métodos (ejemplo):

  • GET para obtener un recurso del servidor
  • POST para crear un recurso del servidor
  • PUT para actualizar un recurso del servidor
  • DELETE para eliminar un recurso del servidor

Y en algunas otras encontraremos exactamente lo contrario (ejemplo):

  • GET para obtener un recurso del servidor del servidor
  • POST para actualizar un recurso del servidor
  • PUT para crear un recurso del servidor
  • DELETE para eliminar un recurso del servidor

En realidad estas definiciones son incorrectas. La diferencia no es que uno se utilice para crear y otro para actualizar. Los dos métodos pueden crear un recurso y los dos métodos pueden actualizarlo.

La diferencia no es que uno se utilice para crear y otro para actualizar, esto es incorrecto

PUT se utiliza para poner un recurso en un lugar especificado (la URL). POST es mucho más general. POST simplemente envía una información al servidor para que éste la trate como considere oportuno. Puede crear un recurso nuevo, devolver una información, puede guardarlo en la base de datos, borrarlo, duplicarlo o modificarlo, puede crear 10 recursos de un tipo y dos de otro o puede no hacer absolutamente nada.

  • PUT pone un recurso en la dirección especificada en la URL. Exactamente en esa dirección. Si no existe, lo crea, si existe lo reemplaza.
  • POST envía datos a una URL para que el recurso en esa URI los maneje.

La diferencia principal, tal como se explica en la RFC 2016 que define el protocolo HTTP 1.1, es el significado que se da a la URL de la petición:

"La diferencia fundamental entre las peticiones POST y PUT se refleja en el diferente significado de la URI de la petición. La URI en un POST identifica el recurso que manejará la entidad que se incluye (los datos). [...]. En cambio, la URI en un PUT identifica la entidad incluida con la petición -- el user agent sabe que URI debe emplearse y el servidor NO DEBE intentar aplicar la petición a un recurso diferente."

Es decir, PUT sólo debe emplearse para crear o reemplazar un recurso (que se incluye en el cuerpo de la petición) en una URL conocida. En terminos REST, conocemos la ID del objeto que vamos a crear/reemplazar y ese objeto (entidad) se incluye en el cuerpo de la petición. Por ejemplo:

PUT a la URL: myServer.com/user/1134

Crea o reemplaza el usuario con ID 1134. Exclusivamente ese. Si queremos crear un usuario nuevo, sin saber su ID, la operación correcta sería POST:

POST a la URL: myServer.com/user

El servidor crearía un nuevo objeto user y le asignaría una ID. Crearía, por ejemplo, el objeto:

myServer.com/user/1235

Esto nos lleva a otro concepto importante: idempotencia. PUT es idempotente, POST no lo es.

Idempotente significa que produce el mismo resultado sin importar cuantas veces se realice la operación.

En el ejemplo que veíamos antes, con el método PUT:

PUT a la URL: myServer.com/user/1134

Estamos definiendo una operación idempotente sobre un recurso concreto. No importa cuantas veces la lancemos, el resultado será siempre el mismo: el recurso identificado como "myServer.com/user/1134" existirá con los datos que se han pasado en la petición. Si no existía se creará. Si ya existía se reemplazará de nuevo. No afecta a nada más.

La operación POST que veíamos antes no es idempotente. Cada vez que se lance la petición al servidor creará un recurso nuevo, asignandole una nueva ID.

POST a la URL: myServer.com/user

Despues de 4 peticiones tendriamos los recursos:

  • myServer.com/user/1235
  • myServer.com/user/1236
  • myServer.com/user/1237
  • myServer.com/user/1238

Resumen

Estos son los puntos más importantes que debemos tener en cuenta:

  • Una petición PUT a una URL afecta únicamente a esa URL. Un POST a una URL puede tener cualquier efecto.
  • En una petición PUT, los datos incluidos en el cuerpo de la petición se toman como una entidad que quedará accesible en la dirección URL de la petición.
  • Solo debemos utilizar PUT cuando sabemos exactamente la url correspondiente al recurso que vamos a crear (o actualizar).
  • PUT es idempotente, POST no es idempotente.

Fuentes:
StackOverflow: PUT vs POST in REST
PUT or POST: The REST of the Story
POST vs. PUT
StackOverflow: What's the difference between a POST and a PUT HTTP REQUEST?

lunes, 22 de julio de 2013

Doble negación binaria (~~) para redondear en JavaScript

Alguna vez te puedes encontrar en JavaScript con una línea como esta:

var mins = ~~(seconds / 60);

¿Que significa el doble símbolo "~~"?¿que hace?

El operador ~

~ es un operador binario de negación o complemento (bitwise NOT operator). Este operador convierte el operando en un entero de 32 bits para luego invertir cada bit individualmente. Los ceros se convierten en unos y los unos en ceros.

El operador doble (~~) se utiliza para redondear, como un equivalente rápido de Math.floor(). Al invertir los bits dos veces quedan igual que antes, pero la conversión a entero permanece (utiliza el método interno ToInt32).

~~5.7;       // => 5 
~~32.18897;  // => 32
~~5.7e1;     // => 57  (podemos utilizar notación exponencial)
~~314e-2;    // => 3

Lo que hace realmente la doble negación binaria es eliminar cualquier número despues de la coma (truncar, más que redondear). Para los número positivos esto es equivalente a Math.floor(), pero para los números negativos no. Para los negativos es equivalente a Math.ceil(), ya que redondea hacia cero:

~~-5.7;                // => -5
Math.floor(-5.7)       // => -6 
Math.ceil(-5.7)        // => -5
~~-32.18897;           // => -32
Math.floor(-32.18897)  // => -33
Math.ceil(-32.18897)   // => -32

Mas diferencias con Math.floor()

Ademas de que el redondeo de números negativos no es igual, si el operando no es convertible a número, no nos va a devolver NAN, sino 0.

~~"abc";     // => 0 
~~null;      // => 0
~~undefined; // => 0
~~{};        // => 0
~~[];        // => 0
~~(1/0);     // => 0
~~false;     // => 0
~~true;      // => 1 //true es convertible a 1

La ganancia de rendimiento con los nuevos motores de JavaScript es mínima y la pérdida de legibilidad del código es importante. Mas que un atajo es un antipatrón, es decir, una mala forma de solucionar un problema.

Fuentes:
Double bitwise NOT (~~)
Tilde or the Floor? Practical use for JavaScript bitwise operators
Stackoverflow: What is the “double tilde” (~~) operator in JavaScript?

miércoles, 17 de julio de 2013

Diferencias entre URI y URL

Hemos escuchado y leido los dos términos miles de veces, ¿tenemos claro su significado?. En realidad son tres los términos implicados en esta enorme confusión: URI, URL y URN. El primero engloba a los otros dos:

Las definiciones son:

  • URI Uniform Resource Identifier (Identificador Uniforme de Recursos)
  • URL Uniform Resource Locator (Localizador Uniforme de Recursos)
  • URN Uniform Resource Name (Nombre Uniforme de Recursos)

Como vemos en la imagen, un identificador (URI) puede ser un localizador (URL), un nombre (URN) o ambas cosas.

Una URN es un nombre único para un recurso, pero no da ninguna información para su localización. Es también una URI, puesto que lo identifica.

Una URL también identifica un recurso (es una URI), pero su característica especial es que permite localizarlo, acceder al recurso que identifica.

Estos son algunos ejemplos de URN sacados del artículo de la wikipedia:

URN corresponde a
urn:isbn:0451450523 El libro El último unicornio, de 1968, identificado por su número ISBN.
urn:isan:0000-0000-9E59-0000-O-0000-0000-2 La película Spider-Man, de 2002, identificada por su número de identificación audiovisual (ISAN).
urn:issn:0167-6423 La revista científica Science of Computer Programming, identificada por su ISSN.
urn:ietf:rfc:2648 IETF's RFC 2648.
urn:lex:eu:council:directive:2010-03-09;2010-19-UE directiva de la Unión Europea utilizando el espacio de nombres Lex URN.

En todos los casos se proporciona una identificación única de un recurso mediante un sistema de nombrado, pero no obtenemos ninguna información respecto a la localización del documento.

Estos son algunos ejemplos de URLs:

http://www.example.com/welcome.html
https://github.com/proj/ascam.git
file:///home/user/myfile.txt

Estas direcciones identifican un recurso y además nos permiten acceder a él, indicando un protocolo y una dirección.

Es importante tener en cuenta que un recurso no tiene porqué ser un documento, una URI puede hacer referencia a un recurso abstracto, un objeto, un servicio temporal, etc.

Fuentes:

W3C: URIs, URLs, and URNs: Clarifications and Recommendations 1.0
URL vs. URI vs. URN: The Confusion Continues
URLs vs. URIs: Differences and Examples
Wikipedia: Uniform resource identifier

viernes, 12 de julio de 2013

¿Puede un ID de HTML ser un número?

¿Es correcto asignar un número como ID de un elemento en HTML?¿nos puede dar problemas?

<div id="1"> - </div>

La respuesta rápida: es correcto en HTML5 e incorrecto en las versiones anteriores. Y para la segunda pregunta, la respuesta es sí, nos puede dar problemas al tratar de asignarle estilos con CSS. Mejor no utilizarlo.

Diferencias entre HTML4.01 y HTML5

Según la especificación HTML4.01, un ID sólo puede empezar con una letra:

ID must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").

Las clases, sin embargo, no tienen esta restricción, no tienen que empezar por una letra para ser válidas.

HTML5 elimina también esta restricción para las IDs:

The value must be unique [...] and must contain at least one character. The value must not contain any space characters.

Las única limitaciones que impone para una ID es que no quede vacía y que no contenga espacios.

Válido para HTML pero no para CSS

El problema viene a la hora de intentar asignar estilos. En CSS los identificadores NO pueden empezar con un número. La siguiente definición no es válida y los estilos no se aplicarán:

#1 {
    border: 1px solid red;
}

El '1' no es un identificador válido para CSS ( CSS2.1 identifiers es la especificación referenciada por CSS3 a la hora de definir los ID válidos ):

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".

Con las clases tenemos el mismo problema, tampoco sería válido como identificador de clase. Ninguno de los siguientes estilos se aplicaría:

.1 {
    border: 1px solid red;
}

#33wrapper {
    background-color: blue;

}

.2box {
    margin: 0 auto;
}

Por lo tanto, pueden usarse si sólo utilizamos la ID para seleccionar el elemento con JavaScript, pero es mejor no utilizarlo si vamos a dar estilos CSS basandonos en esa ID.

Fuentes:
What are valid values for the id attribute in HTML?
CSS character escape sequences

martes, 9 de julio de 2013

Transformar segundos a horas, minutos y segundos

Normalmente, en programación, se trabaja con tiempos en segundos porque es mucho más facil operar con ellos. Sumar o restar tiempos en formato hh:mm:ss es bastante más complicado. Sin embargo, en el momento de presentar cualquier tiempo en pantalla, en el interfaz de usuario, tenemos que transformarlo al formato 'legible'.

El código no es complicado, vamos a partir de que tenemos una variable time con los segundos totales, que tenemos que pasar a otros formatos

var time = 9690;

Veamos como podriamos transformarlo.

Minutos y segundos ( mmm:ss )

En aplicaciones de video y multimedia, es habitual mostrar la duración el contenido en minutos y segundos, por ejemplo 122:33.

var minutes = Math.floor( time / 60 );
var seconds = time % 60;

//Anteponiendo un 0 a los minutos si son menos de 10 
minutes = minutes < 10 ? '0' + minutes : minutes;

//Anteponiendo un 0 a los segundos si son menos de 10 
seconds = seconds < 10 ? '0' + seconds : seconds;

var result = minutes + ":" + seconds;  // 161:30

El string ":" en la última línea nos asegura la conversión implicita a string de las variables si es necesario. El + concatenará texto en vez de sumar números.

Horas, minutos y segundos ( h:mm:ss )

Esta es la conversión mas común para presentar tiempos en pantalla.

var hours = Math.floor( time / 3600 );  
var minutes = Math.floor( (time % 3600) / 60 );
var seconds = time % 60;

//Anteponiendo un 0 a los minutos si son menos de 10 
minutes = minutes < 10 ? '0' + minutes : minutes;

//Anteponiendo un 0 a los segundos si son menos de 10 
seconds = seconds < 10 ? '0' + seconds : seconds;

var result = hours + ":" + minutes + ":" + seconds;  // 2:41:30

El código no es complicado. Lo único que hay que saber es que hay 3600 segundos en una hora.

Fuentes:
StackOverflow

jueves, 4 de julio de 2013

El problema del espacio entre elementos inline e inline-block

A veces, cuando queremos colocar elementos inline o inline-block sin ningún espacio entre ellos, aparece una 'extraña' separación de unos pixels que estropea el diseño que pretendiamos conseguir.

Podemos ver un ejemplo de este problema si queremos hacer la siguiente barra de controles de vídeo:

Los botones son imágenes simples del tipo:

Puesto que las imágenes son elementos inline, podriamos pensar que si las colocamos sin márgenes, quedarán juntas. El div con la barra de progreso (#progressBar) lo haremos inline-block en el CSS y todo debería quedar en línea y sin espacios. El CSS y HTML relevante es el siguiente:

//HTML
...
//CSS #progressBar { display: inline-block; }

No definimos ningún margen ni padding en las imágenes. Deberían quedar juntas pero aparece una separación entre ellas:

¿De donde sale ese espacio?¿cómo podemos eliminarlo?

El culpable es el salto de línea

El problema es el salto de línea entre los elementos inline. Para los navegadores es un espacio en blanco y lo deben mostrar. Si eliminamos el salto de línea en el HTML desaparece la separación:

//HTML
...
//CSS #progressBar { display: inline-block; }

Ahora no hay saltos de línea en el HTML y el diseño aparece correcto:

El problema es que el HTML puede resultar confuso e ilegible. Cuando sólo tenemos unas pocas imágenes el problema no es muy evidente. Si tenemos muchos elementos, que ademas pueden ser divs complejos, conteniendo a su vez muchas etiquetas, esta solución es inaceptable. El código resulta inmantenible.

Comentar los saltos de línea

Una solución para eliminar los saltos de línea y mantener el formato, es utilizar comentarios de HTML para enmascararlos:

//HTML
...
//CSS #progressBar { display: inline-block; }

El espacio depende del tipo y tamaño de letra

La solución/chapuza de asignar un margen negativo a los elementos para compensar el espacio, no es una buena idea. La separación es en realidad un espacio en blanco y su tamaño depende del tipo y tamaño de letra que estemos utilizando. Si cambiamos la letra cambiará la distancia entre los elementos.

No asignes un margen negativo para compensar la separación. Si cambias la letra variará el espacio entre elementos.


Reducir el tamaño de letra a cero

Si reducimos el tamaño de letra a cero, el ancho del espacio también será cero y solucionaremos el problema. Obviamente tendremos despues que redefinir el tamaño de letra adecuado en los elmentos inline o inline-block que lo necesiten.

En nuestro ejemplo vamos a suponer que dentro del div '#progressBar' tenemos que mostrar algún texto:

//HTML
...
//CSS #progressBar { display: inline-block; } #videoControls { font-size: 0; } #videoControls > div { font-size: 10px; }

En el contenedor de los elementos ("#videoControls") definimos font-size: 0 y en los elementos que sea necesario volvemos a definir el tamaño correcto font-size: 10px.

Es importante definir el tamaño en unidades absolutas. No podemos utilizar unidades relativas al valor del padre, como % o em. Una buena solución es utilizar rem ( root em ), que es relativa al valor declarado en el elemento raiz del documento (html).

//CSS
#videoControls {
   font-size: 0;
}

#videoControls > div {
   font-size: 1rem;               
}

Si queremos recuperar el tamaño de fuente para todos los hijos:

//CSS
#videoControls > * {
   font-size: 10px;               
}

NOTA: Este método no funciona en los navegadores de Android pre-Jellybean.

Utilizar float en vez de inline

Esta es la solución más común para diseños complejos. Colocamos los elementos juntos utilizando 'float: left' o 'float: right' que no presentan este problema. Por supuesto hay que conocer y tener en cuenta las particularidades de este tipo de posicionamiento.

En nuestro ejemplo el código quedaría:

//HTML
...
//CSS .right{ float: right; } .left{ float: left; } #videoControls > img, #progressBar { margin: 0; }

El resultado es perfecto:

Es importante eliminar el margen de los elementos para que queden juntos. Algunos elementos presentan valores por defecto, aunque no se los definamos.

Hemos tenido que cambiar el orden de las últimas dos imágenes en el HTML para que aparezcan como queremos. Esta es una de las particularidades del posicionamiento float de las que hablabamos antes. Hay muchas más...