9 - Sintaxis completa de los endpoints en la clase Controller

En el contexto de Spring Boot y en general en el desarrollo de APIs, un "endpoint" se refiere a una URL específica a la que los clientes pueden enviar solicitudes HTTP para realizar operaciones sobre recursos específicos. Los endpoints son puntos de acceso a nuestra aplicación que definen cómo interactúan los clientes (página web, aplicación móvil etc.) con ella.

En una API, los endpoints son los puntos de acceso a los recursos y se utilizan para realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) sobre esos recursos.

Estructura de un Endpoint:

Un endpoint consta de dos partes principales:

  • Ruta (Path): Es la parte de la URL que viene después del nombre del dominio. Define la ubicación del recurso al que se accede. Por ejemplo,
    /chistes, /chistes/{id}, etc.

  • Método HTTP: Es el verbo que indica qué operación se realizará en el recurso. Los métodos HTTP más comunes son GET, POST, PUT y DELETE, utilizados respectivamente para obtener, crear, actualizar y eliminar recursos.

Implementación de Endpoints en Spring Boot

En Spring Boot hemos visto que podemos definir endpoints utilizando anotaciones proporcionadas por el framework. Algunas de las anotaciones más comunes son:

  • @RestController: Anotación que marca una clase como un controlador de Spring MVC que maneja solicitudes HTTP y devuelve datos serializados como respuesta, generalmente JSON.

  • @GetMapping, @PostMapping, @PutMapping, @DeleteMapping: Estas son anotaciones especializadas para métodos de controlador específicos que manejan solicitudes GET, POST, PUT y DELETE respectivamente.

    Ya hemos visto en conceptos anteriores el @GetMapping (en el próximo ejemplo veremos las otras anotaciones y su sintaxis):

        @GetMapping("/chisteazar")
        public Chiste chisteazar() {
            Chiste chisteAzar = chisteService.obtenerChisteAleatorio();        
            return chisteAzar;
        }
        
        @GetMapping("/chistetodos")
        public List <Chiste> chisteTodos() {
            List<Chiste> chistes=chisteService.todos();
            return chistes;
        }
    

Problema

Para concentrarnos en la sintaxis que debemos implementar en la clase controladora y en la clase de servicios, continuaremos almacenando los datos en la memoria ram (ArrayList) y no en una base de datos (luego si veremos como se almacenan los datos en una base de datos)

Seguimos trabajando con el problema de chistes, pero ahora por cada chiste vamos a definir su id (identificador), su texto y su autor.

Vamos a definir todos los endpoints para poder recuperar todos los chistes, un solo chiste al azar, agregar un chiste, borrar un chiste y modificar un chiste.

Luego mediante Postman probaremos el funcionamiento de cada endpoint.

Pasos para crear el proyecto:

  • Creamos el proyecto006 y en el segundo diálogo seleccionamos la dependencia "Spring Web"

  • Creamos las carpetas model, service y controller:

    model service controller

  • Creamos las clases respectivas en cada carpeta:

    Chiste ChisteService ChisteController

    Chiste.java

    package com.example.demo.model;
    
    public class Chiste {
        private int id;
        private String texto;
        private String autor;
    
        public Chiste() {
        }
    
        public Chiste(int id, String texto, String autor) {
            this.id = id;
            this.texto = texto;
            this.autor = autor;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getTexto() {
            return texto;
        }
    
        public void setTexto(String texto) {
            this.texto = texto;
        }
    
        public String getAutor() {
            return autor;
        }
    
        public void setAutor(String autor) {
            this.autor = autor;
        }
    }
    

    Definimos tres atributos en la clase Chiste, los cuales almacenan el código de chiste (id), el texto del chiste propiamente dicho y su autor.

    ChisteService.java

    package com.example.demo.service;
    
    import com.example.demo.model.Chiste;
    import org.springframework.stereotype.Service;
    import java.util.ArrayList;
    import java.util.List;
    
    @Service
    public class ChisteService {
    
        private List<Chiste> chistes;
    
        public ChisteService() {
            chistes = new ArrayList<>();
            chistes.add(new Chiste(1, "¿Qué le dice un 0 a un 8? Bonito cinturón.", "Juan"));
            chistes.add(new Chiste(2, "¿Qué hace una abeja en el gimnasio? ¡Zum-ba!", "Marcos"));
            chistes.add(new Chiste(3, "¿Cuál es el colmo de un electricista? Que su hijo sea un apagado.", "Luis"));
            chistes.add(new Chiste(4, "¿Por qué estás hablando con la pared? ¡Porque la mesa no me responde!", "Anonimo"));
        }
    
        public List<Chiste> todos() {
            return chistes;
        }
    
        public Chiste obtenerChisteAleatorio() {
            return chistes.get((int) (Math.random() * chistes.size()));
        }
    
        public void agregarChiste(Chiste chiste) {
            chistes.add(chiste);
        }
    
        public Chiste obtenerChiste(int id) {
            for (Chiste chiste : chistes) {
                if (chiste.getId() == id) {
                    return chiste;
                }
            }
            return null;
        }
    
        public void actualizarChiste(int id, Chiste nuevoChiste) {
            for (int i = 0; i < chistes.size(); i++) {
                if (chistes.get(i).getId() == id) {
                    chistes.set(i, nuevoChiste);
                    return;
                }
            }
        }
    
        public void eliminarChiste(int id) {
            for (int i = 0; i < chistes.size(); i++) {
                if (chistes.get(i).getId() == id) {
                    chistes.remove(i);
                    return;
                }
            }
        }
    
    }
    

    Creamos la estructura de datos en memoria que almacenará los chistes y todos los métodos para poder agregar nuevos chistes, borrarlos y modificarlos.

    ChisteController.java

    package com.example.demo.controller;
    
    import org.springframework.web.bind.annotation.*;
    
    import com.example.demo.model.Chiste;
    import com.example.demo.service.ChisteService;
    
    import java.util.List;
    
    @RestController
    public class ChisteController {
    
        private final ChisteService chisteService;
    
        public ChisteController(ChisteService chisteService) {
            this.chisteService = chisteService;
        }
    
        // Endpoint para obtener todos los chistes
        @GetMapping("/chistes")
        public List<Chiste> obtenerTodosLosChistes() {
            return chisteService.todos();
        }
    
        // Endpoint para obtener un chiste por su ID
        @GetMapping("/chistes/{id}")
        public Chiste obtenerChistePorId(@PathVariable int id) {
            return chisteService.obtenerChiste(id);
        }
    
        // Endpoint para obtener un chiste aleatorio
        @GetMapping("/chistes/aleatorio")
        public Chiste obtenerChisteAleatorio() {
            return chisteService.obtenerChisteAleatorio();
        }
    
        // Endpoint para agregar un nuevo chiste
        @PostMapping("/chistes")
        public void agregarNuevoChiste(@RequestBody Chiste chiste) {
            chisteService.agregarChiste(chiste);
        }
    
        // Endpoint para actualizar un chiste existente
        @PutMapping("/chistes/{id}")
        public void actualizarChiste(@PathVariable int id, @RequestBody Chiste nuevoChiste) {
            chisteService.actualizarChiste(id, nuevoChiste);
        }
    
        // Endpoint para eliminar un chiste por su ID
        @DeleteMapping("/chistes/{id}")
        public void eliminarChiste(@PathVariable int id) {
            chisteService.eliminarChiste(id);
        }
    
    }
    

    Recordemos que la anotación @RestController de esta clase indica a Spring que los métodos deben mapearse a las solicitudes HTTP y devolver respuestas JSON.

    Métodos endpoints:

    obtenerTodosLosChistes(): Mapea la solicitud HTTP GET a /chistes para obtener todos los chistes.
    obtenerChistePorId(int id): Mapea la solicitud HTTP GET a /chistes/{id} para obtener un chiste por su ID.
    obtenerChisteAleatorio(): Mapea la solicitud HTTP GET a /chistes/aleatorio para obtener un chiste aleatorio.
    agregarNuevoChiste(Chiste chiste): Mapea la solicitud HTTP POST a /chistes para agregar un nuevo chiste.
    actualizarChiste(int id, Chiste nuevoChiste): Mapea la solicitud HTTP PUT a /chistes/{id} para actualizar un chiste existente.
    eliminarChiste(int id): Mapea la solicitud HTTP DELETE a /chistes/{id} para eliminar un chiste por su ID.
    
  • Ahora podemos compilar, lanzar el proyecto y desde Postman comenzar a verificar cada endpoint:

    postman retornar todos los chistes

    Se ejecuta el endpoint:

        // Endpoint para obtener todos los chistes
        @GetMapping("/chistes")
        public List<Chiste> obtenerTodosLosChistes() {
            return chisteService.todos();
        }
    
    postman retornar un chiste por id

    Se ejecuta el endpoint:

        // Endpoint para obtener un chiste por su ID
        @GetMapping("/chistes/{id}")
        public Chiste obtenerChistePorId(@PathVariable int id) {
            return chisteService.obtenerChiste(id);
        }
    

    @GetMapping("/chistes/{id}"): Esta anotación indica que este método manejará las solicitudes HTTP GET en la ruta "/chistes/{id}". La parte {id} en la ruta es una variable de ruta que puede ser cualquier identificador único de un chiste.

    Este método tiene un parámetro @PathVariable int id, que indica que este método toma el valor de la variable de ruta {id} en la URL. Spring Boot automáticamente asignará el valor del {id} de la URL al parámetro id del método.

    postman retornar un chiste aleatorio

    Se ejecuta el endpoint:

        // Endpoint para obtener un chiste aleatorio
        @GetMapping("/chistes/aleatorio")
        public Chiste obtenerChisteAleatorio() {
            return chisteService.obtenerChisteAleatorio();
        }
    
    postman post

    Se ejecuta el endpoint:

        // Endpoint para agregar un nuevo chiste
        @PostMapping("/chistes")
        public void agregarNuevoChiste(@RequestBody Chiste chiste) {
            chisteService.agregarChiste(chiste);
        }
    

    Este método dentro del controlador Spring Boot está diseñado para manejar solicitudes POST en la ruta "/chistes", lo que significa que permite agregar un nuevo chiste.

    @PostMapping("/chistes"): Esta anotación indica que este método manejará las solicitudes HTTP POST en la ruta "/chistes".

    Este método tiene un parámetro @RequestBody Chiste chiste, que indica que este método espera recibir un objeto de tipo Chiste en el cuerpo de la solicitud HTTP. Spring Boot automáticamente convertirá el cuerpo de la solicitud en un objeto de tipo Chiste basado en la estructura de datos JSON recibida en la solicitud.

    El objeto se agrega en el servicio, tengamos en cuenta que mientras no se detenga el servidor el chiste agregado permanece en el ArrayList (pronto veremos la inserción en una tabla de una base de datos). Luego si ejecutamos el endpoint que retorne todos los chistes podemos ver que esta almacenado:

    postman get todos los chistes

    Para borrar un chiste pasando su id:

    postman borrar un chiste por id

    Se ejecuta el endpoint:

        // Endpoint para eliminar un chiste por su ID
        @DeleteMapping("/chistes/{id}")
        public void eliminarChiste(@PathVariable int id) {
            chisteService.eliminarChiste(id);
        }
    

    Finalmente para modificar un chiste:

    postman modificar un chiste por id

    Se ejecuta el endpoint:

        // Endpoint para actualizar un chiste existente
        @PutMapping("/chistes/{id}")
        public void actualizarChiste(@PathVariable int id, @RequestBody Chiste nuevoChiste) {
            chisteService.actualizarChiste(id, nuevoChiste);
        }