Introducción al diseño de una API HTTP (Parte 2 de 4) : HTTP Responses

Este segundo artículo abarca una breve introducción sobre las respuestas HTTP (HTTP Responses).

¿Qué es un HTTP Response?

Un HTTP Response, es la respuesta que devuelve un servidor web luego de recibir y procesar una solicitud HTTP (HTTP Request) de un cliente. La estructura del mensaje del HTTP Response es similar en formato al HTTP Request. Veamos un ejemplo.

La primera línea se denomina “línea de estado” y contiene dos partes de información. El primero es el protocolo, en este caso HTTP 1.1, seguido de un espacio. La segunda información es el estado de la solicitud (status code), que se compone de una representación numérica del estado (un código de estado) analizable por el emisor, seguida de una nueva línea. En el ejemplo, el código de estado HTTP es 200 OK.

Al igual que un HTTP Request, el HTTP Response contiene una serie de pares de headers (clave: valor). El nombre del header está a la izquierda, seguido de dos puntos y un espacio, y luego el valor, seguido de una nueva línea. Se recomienda que los nombres del header inicien con una letra mayúscula. Los headers pueden repetirse si tienen múltiples valores, por ejemplo, puedes repetir el header Set-Cookie nos permite establecer varias cookies en una sola respuesta.

Los headers son seguidos por un cuerpo (body response) opcional, que requiere dos nuevas líneas de la última cabecera antes del inicio del contenido del cuerpo. Casi todas las respuestas incluirán un cuerpo aunque es técnicamente opcional.

Headers del HTTP Response

Al igual que los headers del HTTP Request, los headers del HTTP Response representan metadatos que envía el servidor al cliente. Por lo general, hacemos uso de un header HTTP estándar, pero a veces es necesario inventar uno propio. Para hacerlo, se sugiere prefijar el nombre del header con X-, como por ejemplo X-Request-ID. La siguiente lista representa los headers más comunes utilizados en un HTTP Response.

Cache-Control: especifica la política del mecanismo de caché. Por ejemplo, si usamos el valor no-cache le estamos diciendo al cliente que no almacene en cache el recurso o colección solicitado.

Content-Language: especifica el idioma del contenido, por ejemplo en-US, lo cual indica que el idioma del contenido del HTTP Response está en ingles.

Content-Length: corresponde al tamaño en bytes del contenido (body) del HTTP Response, si se conoce antes de tiempo.

Content-Type: hace referencia al tipo de contenido del cuerpo, por ejemplo, podemos usar el valor “application/json“, de esta manera le estamos diciendo al cliente que el contenido enviado esta en formato JSON.

Date: Fecha y hora del servidor.

Expires: Fecha y hora en que el contenido debe caducar en el cliente.

Server: utilizado para identificar al servidor.

Hablemos de los códigos de estados HTTP

Los códigos de estado HTTP (ó HTTP Status Code) se separan en diferentes rangos numéricos, cada rango representa una clase diferente de estados. En las siguientes secciones de este artículo describiremos cada rangos así como valores comunes dentro de cada rango.

Códigos de Estado HTTP – Informativos: 1xx

El rango 1XX de los códigos de estado (100 – 199) son códigos informativos de estado. Generalmente, estos códigos de estado son muy poco usados, dependerá si el API que construyas, lo requiere.

101 Switching Protocols: usados para websockets.

Códigos de Estado HTTP – Exitosos: 2xx

El rango 2XX de códigos de estado (200 – 299) son códigos de estado satisfactorios. Señalan al cliente que cualquier operación, tal como crear un recurso o simplemente recuperar uno, se ha completado satisfactoriamente. Generalmente estos códigos de estado son los más consumidos y producidos con mayor frecuencia.

200 OK: significa que el procesamiento y respuesta de servidor fue exitoso.

201 Created: significa que el recurso ha sido creado. Por ejemplo, cuando envías un POST Request puedes esperar este código de estado del servidor.

202 Accepted: significa que el recurso fue creado o modificado con éxito pero el cambio es asíncrono.

204 No Content: significa que la solicitud tuvo éxito pero el HTTP Response no tiene un cuerpo (Response body)

Códigos de Estado HTTP – Redirección: 3XX

El rango 3XX de códigos de estado (300 – 399) se utiliza para redirigir al cliente de un lugar a otro. Los HTTP Response con estos códigos de estado no tienen un cuerpo, sino que hacen uso de un header de ubicación. Contiene una URL a la que se debe redirigir el cliente.

301 Moved Permanently: significa que el recurso esta en una nueva ubicación, sugiere ir a la nueva dirección (URL).

302 Found: significa que el recurso está en la nueva ubicación pero siempre compruebe la antigua ubicación.

Códigos de Estado HTTP – Error del Cliente: 4XX

El rango 4XX de códigos de estado (400 – 499) hace referencia a errores originados por el cliente. Es decir, el servidor encontró un error en la solicitud HTTP del cliente. La operación NO debería haber alterado el estado del servidor. Es importante trabajar con estos códigos de estado en tu API HTTP.

400 Invalid Request: significa que existe un error genérico del cliente.

401 Unauthorized: significa que el cliente debe proporcionar un header de autorización.

403 Forbidden: significa que el cliente no esta autorizado a acceder a dicho recurso.

404 Not Found: significa que el recurso no fue encontrado en el servidor, recurso no existe.

405 Method Not Allowed: significa que el endpoint existe pero no admite el método HTTP de la solicitud.

406 Not Acceptable: significa que el servidor no puede generar una respuesta para el tipo de contenido (content-type) enviado en el header Accept.

Códigos de Estado HTTP – Error del Servidor: 5XX

El rango 5XX de códigos de estado (500 – 599) hace referencia a los errores que se producen en el servidor o tal vez la solicitud no fue recibida. Estos errores deben evitarse a toda costa.

500 Internal Server Error: significa que un error genérico ocurrió en el servidor.

501 Not Implemented: significa que el servidor todavía no admite la combinación de método / endpoint de la solicitud del cliente.

503 Service Unavailable: significa que el servidor no está disponible temporalmente. Por ejemplo, cuando hacemos trabajos de mantenimiento o tenemos la base de datos desconectada.

521 Web Server Is Down: significa que un servidor intermediario no puede conectarse al servidor destino.

Fuentes de referencia:

  1. Status Code HTTP

Para conocer la lista completa de artículos, dirigirse a Introducción al diseño de una API HTTP.

Introducción al diseño de una API HTTP (Parte 1 de 4) : HTTP Requests

Este primer artículo abarca una breve introducción sobre las solicitudes HTTP (HTTP Requests).

¿Qué es una Solicitud HTTP?

Creo que primero habría que definir que es HTTP (Hipertext Transfer Protocol). El protocolo HTTP no le debe ser muy indiferente. Si leíste sobre el Modelo OSI, recordarás que este protocolo de comunicación pertenece al nivel de aplicación, que a su vez, esta por encima de otro famoso protocolo, el protocolo TCP (Transmission Control Protocol) que se sitúa en la capa transporte de este modelo. Como todo protocolo de comunicación, el protocolo HTTP define unas normas que debe seguir el emisor que desea transmitir información a un receptor. HTTP utiliza el patrón de request / response (petición / respuesta), lo cual quiere decir, que un emisor (sistema X) realiza una solicitud (request) y un servidor (Sistema Y)  responde (response) a dicha solicitud.

En la siguiente imagen se puede apreciar como un computador (emisor) realiza una solicitud HTTP (HTTP Request) a un servidor (receptor) y este último le responde (HTTP Response) el pedido.

A continuación un ejemplo de las reglas que debe seguir el cliente para que el servidor comprenda su petición HTTP.

La primera línea se llama Línea de Solicitud y contiene tres elementos;

1. El método HTTP (para este ejemplo es POST).

2. Una ruta de acceso (también denominada como endpoint en una API).

3. La versión HTTP (para este ejemplo es HTTP/1.1)

Existen otros métodos HTTP, más adelante se mencionarán.

El contenido que le sigue a la “Línea de Solicitud” se denomina Headers (en español encabezados). Los headers son pares de key – value (clave – valor), donde la clave es la primera palabra, seguido de dos puntos y un espacio y, a continuación, el valor. Los headers están separados por saltos de línea. Es recomendable que las claves de los headers inicien con su primera letra en mayúscula. Los headers pueden repetirse en situaciones en las que es necesario enviar valores duplicados.

Una solicitud HTTP (HTTP Request) solo necesita tener una Línea de Solicitud y algunos headers para ser válido. Esto se aplica principalmente a las solicitudes que utilizan los métodos HTTP GET, DELETE, HEAD y OPTIONS. Sin embargo, también podemos proporcionar un cuerpo de solicitud (request body), que requiere dos líneas nuevas después del último header antes del contenido del cuerpo. Los cuerpos se utilizan principalmente con los métodos HTTP POST, PUT y PATCH.

Hablemos de los Endpoints

Al diseñar una API HTTP RESTful, es importante poder abstraer la funcionalidad de su servicio de tal manera que todas las operaciones puedan representarse realizando operaciones CRUD (Create, Read, Update, Delete) para los diferentes recursos (entidades). Las acciones (verbos HTTP ó métodos HTTP) que se realizan nunca deben ser parte del endpoint.

El enfoque más utilizado es exponer diferentes colecciones de recursos relacionados. Por ejemplo, si su servicio contiene información sobre diferentes empresas y empleados, podría tener una colección llamada empresas y otra colección llamada empleados. Podemos obtener información más específica y hacer referencia a una empresa individual o a sus empleados dentro de cada una de estas colecciones. Como por ejemplo, un empleado puede ser empleado en una empresa. Lo siguiente es cómo podemos representar estos datos y sus relaciones:

Por lo tanto, podemos decir que si queremos obtener la lista de empresas debemos solicitar el endpoint /companies o si queremos obtener información de solo una empresa debemos solicitar el endpoint /companies/{company_id}

Headers HTTP

Habíamos dicho que los headers son un conjunto de pares de clave-valor. Hay muchos headers diferentes que podemos utilizar. Los navegadores web, por lo general, envían varios headers en sus solicitudes HTTP (requests) y los nuevos navegadores están siendo estandarizados todo el tiempo. A continuación una lista de los headers más comunes y su significado, que además debe tener en cuenta para que sean compatibles con el API HTTP que construyas.

Accept: Puede contener una lista de “Tipos de Contenido” (Content-Types) que son aceptados por el cliente (navegador web por ejemplo). Un tipo de contenido muy usado en las APIs  HTTP es application/json. Es decir, el cliente acepta recibir información del servidor en formato JSON.

Accept-Language: Puede contener una lista de idiomas que son aceptados por el cliente. Por ejemplo podemos indicar que el cliente acepta recibir información del servidor en idioma ingles en-US, o en español es-pe.

Content-Length: Es la longitud en bytes del cuerpo de la solicitud. Si se conoce el tamaño del cuerpo de la solicitud, se debe enviar este valor.

Content-Type: Representa al tipo de contenido proporcionado en el cuerpo de la solicitud. Sólo enviar si la solicitud contiene un cuerpo. Por ejemplo podemos indicar en nuestra solicitud HTTP que la información que vamos a enviar al servidor estará en formato JSON.

Host: Este header siempre se debe enviar en las solicitudes HTTP. Representa al Host HTTP del servidor. Las aplicaciones probablemente ignorarán esto, sin embargo, es sobre todo útil para los hosts virtuales y el enrutamiento de solicitudes.

User-Agent: Este header incluye información de identificación del cliente, como por ejemplo el nombre del navegador web de donde se envía la solicitud HTTP.

Estos son solo algunos de los headers estandarizados que existen, pero también hay un montón de headers que puedes agregar de manera ad-hoc. Como regla general, al inventar sus propios headers, el patrón común es prefijarlo con una X por adelante, como por ejemplo X-API-Version para representar la versión del API.

Método GET

El método GET hace referencia a la acción de Leer (Read) del CRUD. Las solicitudes GET (GET requests) se consideran seguras, lo que significa que no deben alterar el estado del servidor. Las solicitudes GET se consideran idempotentes, lo que significa que deben ser repetibles sin causar efectos secundarios. Las solicitudes GET no deben tener un cuerpo (body request).

Cuando se usa el método GET contra una colección (por ejemplo /empresas) significa que deseas recuperar una lista de recursos dentro de esa colección (es decir una lista de empresas). Podemos recuperar todos los recursos o filtrar recursos específicos según los criterios definidos. El siguiente ejemplo es para obtener obtener una lista de empleados:

La solicitud anterior podría traducirse en “Obtener una lista de 10 empleados que trabajan en Twitter que estén en el rango salarial entre 3000 y 8000 (dólares por ejemplo), omitiendo los primeros 20“. El endpoint contiene parámetros de consulta (filtros) que inician luego del símbolo ? (? Key1 = Value1 & Key2 = Value2). Si no se especifican las propiedades de filtrado (parámetros de consulta), el servidor debe responder con todos los recursos coincidentes. Es común que los servidores tengan límites por defecto en los datos devueltos, de lo contrario sería muy abrumador devolver todos los datos de una colección, imagina que hubiesen millones de registros :0.

Método POST

El método POST hace referencia a la acción de Crear (Create) del CRUD. Las solicitudes POST (POST requests) se consideran Inseguras ya que alteran el estado del servidor. Se consideran no idempotentes ya que las solicitudes repetidas darán como resultado la creación de varios recursos. Las solicitudes POST deben tener un cuerpo (body request).

Cuando se usa el método POST contra una colección, el servidor debe crear un nuevo recurso dentro de esa colección.

La primera Linea de Solicitud podemos usarla para agregar una nueva empresa a nuestro servicio.

La segunda Linea de Solicitud podemos usarla para agregar un nuevo empleado a la empresa Twitter.

El cuerpo de la solicitud POST (body request) contendría información sobre el recurso que estamos agregado. Por ejemplo:

Método DELETE

El método DELETE hace referencia a la acción de Eliminar (Delete) del CRUD. Las solicitudes DELETE (DELETE requests) se consideran Inseguras ya que alteran el estado del servidor. Sin embargo, se consideran idempotentes, ya que al repetir esta acción DELETE, no debe tener ningún efecto secundario (es decir si por casualidad vuelves a invocar al endpoint con el verbo DELETE, no va eliminar un recurso que ya fue eliminado). Una solicitud DELETE no debe tener un cuerpo (body request).

La primera Linea de Solicitud es cómo podemos eliminar a un empleado (gchacaltana) de la empresa Twitter.

La segunda Linea de Solicitud es como podemos eliminar a un empleado con identificador gchacaltana.

Métodos PUT/PATCH

Los métodos PUT y PATCH hacen referencia a la acción de Actualizar (Update) del CRUD. Ambos se consideran Inseguros ya que alteran el estado del servidor. Por lo general se consideran Idempotent, es decir si invocamos a la misma solicitud varias veces debe tener el mismo resultado. Esto es cierto en la situación en la que está proporcionando propiedades de recursos y valores que se reemplazarán en un mismo recurso. Sin embargo, si su servicio tiene la capacidad de modificar una propiedad, como incrementar una propiedad, estas solicitudes se convertirían en no idempotentes. Los métodos PUT y PATCH deben tener un cuerpo.

¿Cuál es la diferencia entre PUT y PATCH?

Generalmente, una solicitud PUT realiza una actualización completa, las propiedades faltantes se actualizarán con valores nulos. Por ejemplo, un recurso de empleado podría tener como propiedades un nombre y un rol, pero realizar un PUT y sólo proporcionar un nombre, tendríamos un rol que falta. A diferencia de una solicitud PATCH, estas solicitudes se usan para realizar actualizaciones parciales. Sólo se establecerán las propiedades proporcionadas en el cuerpo de solicitud, las propiedades no listadas conservarán su valor existente. Algunas API sólo admiten PUT o PATCH para realizar cualquier actualización.

Métodos HEAD y OPTIONS

Los métodos HEAD y OPTIONS son un poco especiales. Se podría decir que hacen referencia a la acción Leer (Read) del CRUD pero en realidad no recuperan recursos. Se consideran seguros ya que no alteran el estado de la colección y además son idempotentes, ya que pueden volver a invocarse sin producir malos efectos. Tampoco deben tener un cuerpo de solicitud (body request).

La solicitud HEAD se utiliza para obtener sólo los headers (encabezados) de un recurso. Estos headers pueden contener metadatos útiles como cuando el recurso debe haber caducado. Si el costo de recuperar un recurso es alto (tal vez es un documento grande que es lento para transmitir o tal vez es una costosa llamada de base de datos), entonces podría tener sentido usar una solicitud HEAD en lugar de un GET.

La respuesta (response) del servidor sería:

En una siguiente publicación, hablaremos más a detalle de los HTTP Response.

La solicitud OPTIONS es útil para servicios expuestos a un navegador, por ejemplo, un navegador cliente puede enviar una solicitud OPTIONS y el servidor responde con headers que especifican que métodos HTTP puede utilizar el navegador y que otras funciones son soportadas por el servidor web. Por ejemplo:

El servidor podría enviar una respuesta así (HTTP Response).

El segundo artículo trata sobre una introducción a los HTTP Responses.

Para conocer la lista completa de artículos, dirigirse a Introducción al diseño de una API HTTP.