Returns a string dictionary
jsonify creates an object from Response's Class with the JSON representation of the given arguments. So, cell3.py return a JSON representation
API para gestionar listas de tareas TODO. Implementado en Flask-RESTful
Design an API how manages a list of TODO tasks. The requirements are:
Manage multiples lists. Lists by category. I.e. Shopping, Work, House,...
- Get a list of all TODO items by default from all categories or specifing one category
- Add new item in a specific list
- Remove one item from a specific list
Enunciado Original. Final del documento Actividad A3
Una API de REST, o API de RESTful, es una interfaz de programación de aplicaciones (API o API web) diseñada con los conceptos de la arquitectura REST y permite la interacción con los servicios web de RESTful:
- Recurso: todo dentro de una API RESTful debe ser un recurso.
- URI: los recursos en REST siempre se manipulan a partir de la URI (Identificadores Universales de Recursos).
- Acción: todas las peticiones a tu API RESTful deben estar asociadas a uno de los verbos de HTTP: GET, POST, PUT y DELETE.
Las características de un sistema REST están definidas por seis reglas de diseño:
- Cliente-Servidor: Debe existir una separación entre el servidor que ofrece un servicio y el cliente que lo consume.
- Stateless: Cada solicitud de un cliente debe contener toda la información requerida por el servidor para llevar a cabo la solicitud. El servidor no puede almacenar información proporcionada por el cliente en una solicitud y utilizarla en otra solicitud.
- Cacheable: el servidor debe indicar al cliente si las solicitudes se pueden almacenar en caché o no.
- Sistema en capas: el cliente puede acceder a los recursos del servidor de forma indirecta a través de otras capas, como un proxy o un equilibrador de carga sin que el cliente tenga que hacer nada diferente.
- Interfaz uniforme: el servidor proporcionará una interfaz uniforme para acceder a los recursos sin definir su representación.
- Código a pedido (opcional): los servidores pueden proporcionar código ejecutable o scripts para que los clientes lo ejecuten en su contexto.
Para el consumo de los servicios que ofrece la API REST es necesario definir los recursos. Al tratarse de una lista de tareas, mi único recurso expuesto será tasca, el cual tiene los siguientes campos:
- id: URI único para la tarea. Tipo int.
- categoria: Descripción corta de la tarea. Tipo string.
- descripcio: Descripción larga de la tarea. Tipo string.
- realitzada: Estado de finalización de la tarea. Tipo boolean.
Método HTTP | Endpoint | Descripció |
---|---|---|
GET | /tasca | Lista de todas las tareas |
GET | /tasca/int:tasca_id | Vsualización de una tarea específica |
GET | /tasca/categoria/string:cat | Lista de todas las tareas de una determinada categoría |
GET | /tasca/fet/0 | Lista todas las tareas pendientes |
GET | /tasca/fet/1 | Lista todas las tareas realizadas |
PUT | /tasca/int:tasca_id | Actualiza una tarea determinada. 'categoria', 'descripcio' y 'realitzada' |
POST | /tasca | Añade una nueva tarea |
DELETE | /tasca/esborra/int:tasca_id | Eliminar una tarea determinada |
Existen muchas formas de probar una API REST, algunas sofisticadas como Postman (app o extensión) o YARC! Yet Another REST Client! (extensión de Chrome), otras, más rudimentarias, pero no por ello menos efectivas, por ejemplo, la herramienta por línea de comandos cURL. Las pruebas por tema de agilidad las he realizado con YARC! pero para una completa comprensión de la API mostraré el estilo de codificación con los comandos de cURL:
Con el fin de facilitar las pruebas he definido una serie de registros (diccionarios) por defecto. El formato de intercambio de datos que emplearé por defecto será JSON:
id | categoria | descripcio | realitzada (boolean) |
---|---|---|---|
1 | Compras | Léche, Hüevos, Pizza, Queso | False |
2 | Universidad | Acabar práctica AA.AA. | False |
3 | Trabajo | Entregar informe semanal | False |
4 | Universidad | Preparar examen ADIU | False |
5 | Compras | Calgon, miel, suavizante | False |
6 | Trabajo | Revisión de elementos FDS | False |
Tras iniciar la aplicación
$python3 todolist.py
Abrimos sesión desde otro terminal (local o remoto perteneciente a la LAN) e iremos invocando el comando curl para la realización de las peticiones:
$curl http://localhost:9999/tasca
$curl http://localhost:9999/tasca/4
$curl http://localhost:9999/tasca/categoria/Universidad
$curl http://localhost:9999/tasca/fet/0
Como todas están pendientes el resultado será el mismo que la lista de todas las tareas.
En este caso, debemos indicar que el encabezado que le pasaremos será tipo json (opción -H) y el método HTTP PUT (opción -X). Los datos los especificamos con -d (o --data) y el formato es entre comillas simples. Destacar que JSON reconoce true y false como operaciones booleanas, en cambio, Python las ve como True o False
$curl -H "Content-Type: application/json" -d '{"realitzada":"true"}' -X PUT http://localhost:9999/tasca/3
Si todo ha sido correcto nos devolverá:
{"Tasca actualitzada": {"categoria":"Trabajo","descripcio":"Entregar informe semanal", "id": 3, "realitzada": true}}
$curl -H "Content-Type: application/json" -d '{"categoria":"Trabajo","descripcio":"Cerrar línea GALILEA","realitzada":"true"}' -X POST http://localhost:9999/tasca
Si todo ha sido correcto nos devolverá:
{"Creada": "Nova tasca", "tasca":{"categoria":"Trabajo","descripcio":"Cerrar línea GALILEA", "id": 7, "realitzada": true}}
$curl -X DELETE http://localhost:9999/tasca/esborra/2
Si el resultado es satisfactorio nos devolverá un json:
{"eliminat": true}
A la hora de la realización de la práctica y aplicar la metodología de diseño REST me he encontrado varios puntos que destaco a continuación:
- Las peticiones GET al ser una operación de lectura no acepta parámetros para filtrar la información que requiero del servidor. Por ejemplo, no puedo obtener una lista de tareas de la categoría Trabajo haciendo un GET /tasca/categoria pasándole un Payload '{"categoria":"Trabajo"}'. El request.json() me devuelve un valor null siempre. Por lo que para mantener el estilo REST la petición debe hacerse GET /tasca/categoria/Trabajo
- En mi lista de tareas he querido emplear la variable booleana realitzada para filtrar tareas hechas y pendientes. JSON ve a esta variable como true/false, pero Python las ve como True/False, por lo que esta diferencia, aunque pequeña es particularmente insidiosa. Una por el tiempo que me ha llevado darme cuenta del detalle y otra por tener que realizar un tratamiento cada vez que se tenga que manipular.
- La última y no por ello la menos importante es que la extensión Flask-RESTful en la práctica ha creado una capa más de restricciones al crear la API. Y es que los métodos que tienen las clases de cada recurso que añado sólo pueden constar de las palabras clave de las operaciones/verbos HTTP. Con esto quiero decir que para mi método GET puedo definir: def get(self,tasca_id): pero no puedo usar def get_tarea(self,tasca_id):. get como método le sirve pero get_tarea no existe como método http y no accederá. Los métodos que haya en las clases se deben definir exactamente como los verbos HTTP.
Para la realización de la API en Python he requerido de los siguientes "micro" framework y extensiones:
- Flask 2.0.2 (micro framework para Python)
- Flask-Jsonpify 1.5.0 (extensión para Flask)
- Flask-RESTful 0.3.9 (extensión para Flask)
Restful with Python: Requests and Flask
Wikipedia. Representational state transfer
Básicamente, REST es un estilo/filosofía de programación donde la información se manipula mediante recursos, mientras que en RPC expones operaciones.
Una solicitud HTTP básica consta de un método (method) y un recurso (endpoint). El método HTTP (o verbo) indica lo que desea hacer (GET / POST / PUT / DELETE).
Así, en RPC expones operaciones para manipular datos a través de HTTP y aunque no hay reglas:
- El recurso contiene el nombre de la operación que desea invocar.
- Generalmente solo usa verbos HTTP GET y POST.
REST en cambio, expone los datos como recursos que manipula a través del protocolo HTTP utilizando el verbo HTTP correcto:
- El endpoint contiene el recurso que manipula.
- Se suele usar la analogía CRUD para explicar los principios de las solicitudes REST. El verbo HTTP indica lo que desea hacer (Crear / Leer / Actualizar / Eliminar).
Así por ejemplo:
Acción | RPC (operación) | REST (recurso) |
---|---|---|
Registrarse | POST /signup | POST /persons |
Eliminar | POST /resign | DELETE /persons/1234 |
Datos de una persona | GET /readUser?personid=1234 | GET /persons/1234 |
Mostrar de items de una persona | GET /readUsersItemsList?userid=1234 | GET /persons/1234/items |
Añadir un item a una persona | POST /addItemToUsersItemsList | POST /persons/1234/items |
Actualizar un item | POST /modifyItem | PUT /items/456 |
Borrar un item | POST /removeItem?itemId=456 | DELETE /items/456 |