Skip to content

Instantly share code, notes, and snippets.

@nhocki
Created September 19, 2013 19:47
Show Gist options
  • Save nhocki/6628875 to your computer and use it in GitHub Desktop.
Save nhocki/6628875 to your computer and use it in GitHub Desktop.

Guías para las API de servicios

En esa sección se proveen las guías y ejemplos para las API de los servicios del portal de Medellín Digital. Esta guía fomenta la consistencia, mantenibilidad y mejores prácticas a través de los difertenes servicios del portal.

Este documento se basa en:

Reglas Generales para Servicios RESTful

REST es un estilo de arquitectura propuesto por Roy Fielding en el año 2000, que busca desacoplar el cliente y el servidor de una aplicación.

Restricciones

La arquitectura REST tiene cinco restricciones requeridas:

  • Cliente-Servidor: Una clara separación entre el cliente el servidor. Esto implica, por ejemplo, que los clientes no son responsables de almacenar datos y que los servidores no se encargan de la interfaz de usuario.

  • Sin estado: Toda comunicación entre el cliente y el servidor es independiente del contexto. En ninguna parte se almacena información sobre interacciones pasadas. Esto implica que cada petición del cliente al servidor debe incluir toda la información necesaria para contextualizar al servidor.

  • Cacheable: Las respuestas que envía el servidor pueden ser guardadas en cache. Debe existir un mecanismo para invalidar la respuesta y conseguir una verisón más reciente.

  • Sistema por capas: La petición puede pasar por varios sistemas antes de generar una respuesta. Estas pueden incluir un caché, un balanceador de carga, un proxy y finalmente el servidor. Esto tiene que ser transparente para el cliente.

  • Interfaz uniforme: Simplifica y desacopla la arquitectura entre el cliente y el servidor. Así, cada componente puede evolucionar de forma independiente.

Principios de la Interfaz

La interfaz uniforme debe tener en cuenta los siguientes principios:

  • Identificación de Recursos: Los recursos son elementos de información que pueden ser accedidos por un identificador global (URI). Esto implica que el servidor no debe enviar los registros en base de datos "crudos", sino objetos que forman parte del dominio de la aplicación. Los recursos son enviados en una representación específica (XML, JSON, HTML).

  • Manipulación de los recursos a través de las representaciones: Cuando un cliente adquiere la representación de un recurso, tiene la información suficiente para modificarlo o eliminarlo en el servidor si tiene los permisos suficientes.

  • Mensajes auto-descriptivos: Todos los mensajes que se transmiten entre los componentes, tienen suficiente información para informar como deben ser manipulados. Esto incluye, por ejemplo, el formato (JSON, XML) en el que se espera recibir la representación del recurso.

  • Hypermedia como el motor del estado de la aplicación: HATEOAS por su nombre en inglés, significa que en todo momento, el servidor es quien envía al cliente las transiciones que puede realizar desde el estado en el que se = encuentra. Esto implica que la única dirección que conoce el cliente es la inicial (root de la API). Todas las otras peticiones se hacen dependiendo de la información enviada por el servidor.

REST Pragmático

Aún cuando lo ideal sería tener APIs 100% RESTful, se facilitan las siguientes excepciones:

Identificadores de los Servicios (URLs)

  • Una URL identifica un recurso.

  • Una URL nunca tiene verbos. Siempre son sustantivos.

  • Usar sustantivos en plural para la consistencia.

  • Usar los verbos HTTP (GET, POST, PUT, DELETE) para operar sobre los recursos y colecciones.

  • Nunca se deben tener URLs con más de dos niveles de profundidad:

    • Mal: https://portal.ejemplo.com/api/v1/eventos/32/fotos/332/comentarios
    • Bien: https://portal.ejemplo.com/api/v1/fotos/332/comentarios
  • La versión debe ser siempre el primer parámetro en la URL https://portal.ejemplo.com/api/v32/.

  • La versión siempre tiene que ser un número entero.

  • El formato debe ser de la forma api/v3/recurso/{identificador}.{formato}

Ejemplos de URLs

Buenas URLs

  • Una lista de eventos: https://api.ejemplo.com/v1/eventos.json

  • Filtrando colecciones:

    • https://api.ejemplo.com/v1/eventos.json?ano=2013&order=desc
    • https://api.ejemplo.com/v1/picoyplaca.json?placa=2
  • Un solo recurso: https://api.ejemplo.com/v1/eventos/3-pais-paisa.json

  • Recursos anidados: Por ejemplo, todas las fotos un evento específico.

    • https://api.ejemplo.com/v1/eventos/3-pais-paisa/fotos.json
  • Crear una nueva foto en un evento específico:

    • POST https://api.ejemplo.com/v1/eventos/3-pais-paisa/fotos.json

Malas URLs

  • Nombre en singular: https://api.ejemplo.com/v1/evento/3-pais-paisa.json

  • Verbos en la URL: https://api.ejemplo.com/v1/eventos/3-pais-paisa/editar

  • Filtros como parte de la URL: https://api.ejemplo.com/v1/eventos/2013

Verbos HTTP

Los verbos utilizados al realizar la petición, darán al servidor el contexto necesario para saber qué manipulación se quiere hacer con los recursos.

HTTP METHOD POST GET PUT DELETE
CRUD OP CREATE READ UPDATE DELETE
/perros Crea un perro nuevo Lista de perros Actualización en bloque borra todos los perros
/perros/1234 Error Muestra el perro Si existe, actualiza el perro; sino, error borra al perro 1234

(Ejemplo de Web API Design, de Brian Mulloy, Apigee.)

Respuestas

  • No poner valores en las llaves.

  • No exponer valores privados ni nombres específicamente internos ("node", "taxonomy").

  • Los metadatos deben ser específicos a la respuesta. No a los miembros de ella.

  • Todos los recursos, en colecciones o no, deben tener un identificador relacionado donde el cliente puede pedir información sobre este recurso específico.

  • Todas las respuestas deben tener "metadata". Si es JSON, esto debe ser un objeto aparte. En ATOM (XML) y HTML se pueden utilizar los elementos de hypermedia definidos en los estándares.

Buena Respuesta:

GET /v1/articulos/32-nuevo-portal/etiquetas.json

 {
   "tags": [
     {"nombre": "Medellín Digital", "url": "https://api.ejemplo.com/v1/etiquetas/324-medellin-digital"},
     {"nombre": "Innovación", "url": "https://api.ejemplo.com/v1/etiquetas/24"}
   ],
   "metadata": {
     "links": [
      {"rel": "self", "url": "/v1/articulos/32-nuevo-portal/etiquetas.json"},
      {"rel": "articulo", "url": "/v1/articulos/32-nuevo-portal.json"}
     ]
   }
 }

Mala Respuesta:

GET /v1/articulos/32-nuevo-portal/etiquetas.json

 {
   "tags": [
     {"324": "Medellín Digital"},
     {"24": "Innovación"}
   ]
 }

GET /v1/articulos/32-nuevo-portal/etiquetas.json

 {
   "tags": ["Medellín Digital", "Innovación"]
 }

Errores

Las respuestas de errores deben incluir el código HTTP del error, un mensaje para los desarrolladores y uno para los usuarios (cuando sea necesario), un código de error interno (si aplica), vínculos donde los desarrolladores pueden encontrar información adicional (si aplica). Por ejemplo:

{
  "status" : "400",
  "developerMessage" : "Mensaje claro explicando al desarrollador el problema.
                        Dar sugerencias de como resolver el problema.",
  "userMessage" : "Mensaje para mostrar a los usuarios, si es necesario.",
  "errorCode" : "444444",
  "mas info" : "http://www.example.gov/developer/path/to/help/for/444444,
   http://drupal.org/node/444444",
}

Códigos HTTP en las respuestas

HTTP tiene muchos códigos para responder a las peticiones. Los más utilizados son:

  • 200 - OK: La operación fue completada con éxito.
  • 201 - Creado: Se creó un recurso (se debe enviar en la respuesta).
  • 304 - No modificado: El caché no ha expirado.
  • 400 - Solicitud incorrecta: La solicitud contiene sintaxis errónea y no debería repetirse.
  • 401 - No autorizado: El usuario no se ha autenticado o ha fallado en hacerlo.
  • 403 - Prohibido: La solicitud fue legal, pero el servidor se rehúsa a responderla. En contraste a una respuesta 401 No autorizado, la autentificación no haría la diferencia.
  • 500 - Error Interno: El servidor ha sufrido un error interno.

La lista completa se encuentra aquí.

Ejemplos de Peticiones y Respuestas

Recursos

  • GET /articulos
  • GET /articulos/{id}
  • POST /articulos

GET /articulos

GET /v1/articulos.json

{
  "metadata": {
    "links":{
      [
        {"rel": "next", "url": "https://api.ejemplo.com/v1/articulos.json?=page2"},
        {"rel": "last", "url": "https://api.ejemplo.com/v1/articulos.json?=page7"},
        {"rel": "self", "url": "https://api.ejemplo.com/v1/articulos.json"}
      ]
    }
  }
  "articulos": [
    {
      "id": 1,
      "url": "https://api.ejemplo.com/v1/articulos/1-hola.json",
      "titulo": "La innovación en Medellín",
      "contenido": "El contenido del articulo",
      "tags": [],
      "autor": {
        "nombre": "Nicolás Hock Isaza",
        "url": "https://api.ejemplo.com/v1/autores/323.json"
      },
      "creado": "Agosto 20, 2013"
    },
    {
      "id": 43,
      "url": "https://api.ejemplo.com/v1/articulos/43.json",
      "titulo": "Pico y Placa segundo semestre 2013",
      "contenido": "El contenido del articulo",
      "tags": [
        {"nombre": "pico y placa", "url": "https://api.ejemplo.com/v1/etiquetas/24.json"}
      ],
      "autor": {
        "nombre": "Alejandra Montoya",
        "url": "https://api.ejemplo.com/v1/autores/1.json"
      },
      "creado": "Juli 10, 2013"
    }
  ]
}

GET /articulos/{id}

GET /v1/articulos/1-hola.json

{
  "metadata": {
    "links":{
      [
        {"rel": "self", "url": "https://api.ejemplo.com/v1/articulos/1-hola.json"}
      ]
    }
  }
  "articulo":{
    "id": 1,
    "url": "https://api.ejemplo.com/v1/articulos/1-hola.json",
    "titulo": "La innovación en Medellín",
    "contenido": "El contenido del articulo",
    "tags": [],
    "autor": {
      "nombre": "Nicolás Hock Isaza",
      "url": "https://api.ejemplo.com/v1/autores/323.json"
    },
    "creado": "Agosto 20, 2013"
  }
}

POST /articulos

POST /v1/articulos.json

{
  "titulo": "Un nuevo artículo",
  "contenido": "El contenido del nuevo articulo",
  "autor": {
    "nombre": "Juan Guillermo Lalinde"
  }
  "tags": [
    {"nombre": "innovacion"}, {"nombre": "pico y placa"}
  ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment