En este caso de estudio, vamos a diseñar y desarrollar una API RESTful para una red social básica. La API permitirá a los usuarios registrarse, iniciar sesión, crear publicaciones, seguir a otros usuarios y dar "me gusta" a las publicaciones. Este ejercicio práctico consolidará los conceptos aprendidos en los módulos anteriores.

Objetivos del Caso de Estudio

  1. Diseñar los recursos y URIs necesarios para la API.
  2. Implementar los métodos HTTP adecuados para cada recurso.
  3. Manejar la autenticación y autorización de usuarios.
  4. Gestionar errores y respuestas adecuadas.
  5. Probar y validar la API.

  1. Diseño de Recursos y URIs

Recursos Principales

  1. Usuarios
  2. Publicaciones
  3. Seguidores
  4. Likes

Estructura de URIs

Recurso URI Método HTTP Descripción
Usuarios /users POST Crear un nuevo usuario
Usuarios /users/{id} GET Obtener información de un usuario
Usuarios /users/{id} PUT Actualizar información de un usuario
Usuarios /users/{id} DELETE Eliminar un usuario
Publicaciones /posts POST Crear una nueva publicación
Publicaciones /posts/{id} GET Obtener una publicación
Publicaciones /posts/{id} DELETE Eliminar una publicación
Seguidores /users/{id}/followers GET Obtener seguidores de un usuario
Seguidores /users/{id}/follow POST Seguir a un usuario
Seguidores /users/{id}/unfollow POST Dejar de seguir a un usuario
Likes /posts/{id}/likes POST Dar "me gusta" a una publicación
Likes /posts/{id}/likes DELETE Quitar "me gusta" de una publicación

  1. Implementación de Métodos HTTP

Crear un Nuevo Usuario

from flask import Flask, request, jsonify

app = Flask(__name__)

users = []

@app.route('/users', methods=['POST'])
def create_user():
    user_data = request.get_json()
    new_user = {
        'id': len(users) + 1,
        'username': user_data['username'],
        'email': user_data['email'],
        'password': user_data['password']  # En una aplicación real, nunca almacenar contraseñas en texto plano
    }
    users.append(new_user)
    return jsonify(new_user), 201

if __name__ == '__main__':
    app.run(debug=True)

Explicación:

  • Método HTTP: POST
  • URI: /users
  • Función: create_user recibe datos JSON del nuevo usuario, los almacena en una lista y devuelve el usuario creado con un código de estado 201.

Obtener Información de un Usuario

@app.route('/users/<int:id>', methods=['GET'])
def get_user(id):
    user = next((u for u in users if u['id'] == id), None)
    if user:
        return jsonify(user)
    return jsonify({'error': 'User not found'}), 404

Explicación:

  • Método HTTP: GET
  • URI: /users/{id}
  • Función: get_user busca un usuario por su ID y lo devuelve en formato JSON. Si no se encuentra, devuelve un error 404.

Crear una Nueva Publicación

posts = []

@app.route('/posts', methods=['POST'])
def create_post():
    post_data = request.get_json()
    new_post = {
        'id': len(posts) + 1,
        'user_id': post_data['user_id'],
        'content': post_data['content'],
        'likes': 0
    }
    posts.append(new_post)
    return jsonify(new_post), 201

Explicación:

  • Método HTTP: POST
  • URI: /posts
  • Función: create_post recibe datos JSON de una nueva publicación, la almacena en una lista y devuelve la publicación creada con un código de estado 201.

Seguir a un Usuario

follows = []

@app.route('/users/<int:id>/follow', methods=['POST'])
def follow_user(id):
    follow_data = request.get_json()
    new_follow = {
        'follower_id': follow_data['follower_id'],
        'followed_id': id
    }
    follows.append(new_follow)
    return jsonify(new_follow), 201

Explicación:

  • Método HTTP: POST
  • URI: /users/{id}/follow
  • Función: follow_user recibe datos JSON del seguidor y el usuario a seguir, los almacena en una lista y devuelve la relación de seguimiento creada con un código de estado 201.

Dar "Me Gusta" a una Publicación

@app.route('/posts/<int:id>/likes', methods=['POST'])
def like_post(id):
    post = next((p for p in posts if p['id'] == id), None)
    if post:
        post['likes'] += 1
        return jsonify(post)
    return jsonify({'error': 'Post not found'}), 404

Explicación:

  • Método HTTP: POST
  • URI: /posts/{id}/likes
  • Función: like_post incrementa el contador de "me gusta" de una publicación y devuelve la publicación actualizada. Si no se encuentra la publicación, devuelve un error 404.

  1. Autenticación y Autorización

Para simplificar, utilizaremos un sistema básico de autenticación con tokens. En una aplicación real, se recomienda usar OAuth o JWT.

Generar un Token de Autenticación

import uuid

tokens = {}

@app.route('/login', methods=['POST'])
def login():
    login_data = request.get_json()
    user = next((u for u in users if u['username'] == login_data['username'] and u['password'] == login_data['password']), None)
    if user:
        token = str(uuid.uuid4())
        tokens[token] = user['id']
        return jsonify({'token': token})
    return jsonify({'error': 'Invalid credentials'}), 401

Explicación:

  • Método HTTP: POST
  • URI: /login
  • Función: login verifica las credenciales del usuario y, si son correctas, genera un token único y lo devuelve. Si las credenciales son incorrectas, devuelve un error 401.

Middleware de Autenticación

from functools import wraps

def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token or token not in tokens:
            return jsonify({'error': 'Token is missing or invalid'}), 401
        return f(*args, **kwargs)
    return decorated

Explicación:

  • Función: token_required es un decorador que verifica la presencia y validez del token en las cabeceras de la solicitud. Si el token es inválido o falta, devuelve un error 401.

Aplicar Middleware a Rutas Protegidas

@app.route('/posts', methods=['POST'])
@token_required
def create_post():
    # Código de la función create_post
    pass

Explicación:

  • Función: create_post ahora está protegida por el middleware token_required, asegurando que solo los usuarios autenticados puedan crear publicaciones.

  1. Manejo de Errores y Respuestas

Ejemplo de Manejo de Errores

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Resource not found'}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({'error': 'Internal server error'}), 500

Explicación:

  • Función: not_found y internal_error manejan errores 404 y 500 respectivamente, devolviendo mensajes de error en formato JSON.

  1. Pruebas y Validación

Pruebas con Postman

  1. Crear un nuevo usuario: Enviar una solicitud POST a /users con datos JSON del usuario.
  2. Iniciar sesión: Enviar una solicitud POST a /login con las credenciales del usuario.
  3. Crear una publicación: Enviar una solicitud POST a /posts con datos JSON de la publicación y el token de autenticación en las cabeceras.
  4. Dar "me gusta" a una publicación: Enviar una solicitud POST a /posts/{id}/likes con el token de autenticación en las cabeceras.

Validación de la API

  • Verificar que las respuestas sean correctas y contengan los datos esperados.
  • Asegurarse de que los errores se manejen adecuadamente y se devuelvan los códigos de estado correctos.

Conclusión

En este caso de estudio, hemos diseñado y desarrollado una API RESTful para una red social básica. Hemos cubierto la creación de recursos, la implementación de métodos HTTP, la autenticación y autorización, el manejo de errores y la validación de la API. Este ejercicio práctico refuerza los conceptos aprendidos y proporciona una base sólida para desarrollar APIs RESTful más complejas en el futuro.

© Copyright 2024. Todos los derechos reservados