El mundo del desarrollo de software es vasto, y una herramienta muy valiosa en la construcción de aplicaciones es la capacidad de exponer APIs de manera eficiente. Spring Framework nos facilita esta tarea proporcionándonos varias anotaciones que nos permiten publicar nuestros servicios rápidamente y con menos complicaciones. En esta clase exploraremos cómo usar las anotaciones @GetMapping, @PostMapping y @DeleteMapping.
¿Cómo usar GetMapping para obtener información?
Para obtener información de nuestro servidor, utilizamos la anotación @GetMapping. Esta anotación nos permite vincular un método a una solicitud HTTP GET. Por ejemplo, si queremos publicar un servicio que recupere una lista completa de productos, simplemente añadimos @GetMapping al método correspondiente, especificando la ruta de acceso adecuada:
Cuando implementamos el servicio y lo lanzamos, podemos verificar su correcto funcionamiento desde el navegador o mediante herramientas como Postman. Una solicitud GET sencilla nos devolverá la lista de productos.
¿Cómo recuperar un producto específico por su ID?
Recuperar un producto por su ID es igualmente sencillo, pero requiere un enfoque ligeramente distinto: debemos incluir una variable de ruta en la anotación @GetMapping. Usamos {} para indicar parámetros de ruta dinámicos y @PathVariable para mapearlos al método pertinente:
Asegúrate de diferenciar entre varias rutas posibles para evitar conflictos, por ejemplo, al utilizar identificadores de categorías.
¿Cómo guardar y actualizar información con PostMapping?
Para enviar información nueva al servidor, @PostMapping es la anotación que debemos utilizar, indicando que la información se envía en el cuerpo de la solicitud:
Con esto, recibimos un nuevo objeto Product que se procesa y guarda en el sistema. Asegúrate de formatear las solicitudes JSON correctamente en herramientas como Postman.
¿Cómo eliminar registros con DeleteMapping?
Cuando necesitamos eliminar un recurso, @DeleteMapping es la solución. Como en operaciones de obtención de datos, utilizamos la variable de ruta para especificar qué recurso debe eliminarse:
Esta llamada posiblemente retornará un valor booleano indicando si la operación fue exitosa.
Exponer APIs utilizando Spring es una tarea accesible y altamente efectiva. Con estos ejemplos y el poderoso conjunto de herramientas de Spring, puedes empezar a crear servicios robustos y eficientes para tu aplicación. ¡Continúa explorando y desarrollando, ya que siempre hay nuevas técnicas y mejoras esperando ser descubiertas!
Queda mejor ya que a la hora de definir REST API deberian estar enfocadas a resources y no a operaciones porque para estas, estan los http methods
Profe segun las buenas practicas para exponer servicio NO se debe poner delete ni save como parte de la url ya que estos son parte del verbo del http?
Otro punto porque no se usa LOMBOCK para los setter y getters?
Tienes razón, Eliana! debería bastar con el método HTTP que es usado para cada operación; la hicimos acá así para usar las anotaciones. Gracias por tu recomendación.
Lo otro es que no utilizamos Lombok porque quise mantener lo más puro posible el proyecto. Me gusta usar Lombok en mis proyectos :).
Hola, encontré la razón para el error 404, en mi caso se debe a que añadí un parámetro en la anotación de la clase del método main:
@SpringBootApplication(scanBasePackages={"com.platzi.market.persistence.mapper.ProductMapper"})Debe dejarse sólo la anotación @SpringBootApplication y solucionar el tema del Mapper diferente.Al parecer el plugin tiene inconvenientes en eclipse y por eso no genera el Impl de cada mapper.Esto lo solucioné con dos cosas en eclipse:1.Clic derecho sobre el proyecto -> propiedades ->JavaCompiler->AnnotationProcessing->Se debe checkear Enable project specific settings
2.Clic derecho sobre el proyecto -> propiedades ->JavaCompiler->AnnotationProcessing->FactoryPath->Se debe checkear Enable project specific settings
->AddExternalJar->Aquí buscar el jar del plugin es una ruta similar:C:\Users\pepito\.gradle\caches\modules-2\files-2.1\org.mapstruct\mapstruct-processor\1.4.2.Final\e55bd90d51cddd638c07d5bd89fc7535d4e3d069\mapstruct-processor-1.4.2.FinalAhoraAplicar y CerrarCréditos:[](https://stackoverflow.com/questions/45518161/how-to-get-eclipse-to-generate-mapstruct-mappers-using-gradle)
Tengo el mismo problema, lástima que esta solución no funciona con Maven :(, será volver a empezar el proyecto, con Gradle esta vez.
Gracias
Se me ocurrio el update de la siguiente forma, pero pienso que debe haber una mejor manera de hacerlo
package com.platzi.market.web.controller;import com.platzi.market.domain.Product;import com.platzi.market.domain.service.ProductService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;import java.util.Optional;@RestController@RequestMapping("/products")publicclassProductController{ @AutowiredprivateProductService productService; @GetMapping("/all")publicList<Product>getAll(){return productService.getAll();} @GetMapping("/{id}")publicOptional<Product>getProducto(@PathVariable("id") long productId){return productService.getProduct(productId);} @GetMapping("/category/{categoryId}")publicOptional<List<Product>>getByCategory(@PathVariable("categoryId") long categoryId){return productService.getByCategory(categoryId);} @PostMapping("/save")publicProductsave(@RequestBodyProduct product){return productService.save(product);} @PutMapping("/{id}")publicvoidupdate(@PathVariable("id") long productId, @RequestBodyProduct product){ productService.update(productId, product);} @DeleteMapping("/delete/{id}")public boolean delete(@PathVariable("id") long productId){return productService.delete(productId);}}
Me parece bien cumple su proposito, solo queria saber si agregaste el metodo en ProductRepository y ProductoRepository ya que estamos hablando en terminos de dominio. Te comparto mi metodo update.
Con solo el codigo qu eagrega William Alexander agrego el Put??
o debo dirigirme a otra parte??
y escribir mas
A quienes les sale este error:
ERROR 500, (...) Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.platzi.market.persistence.entity.Producto#0]] with root cause (...)
Solución 01: Observe que lo corregían validando si idProducto=0 y le asignaban null y posterior: return (...) uctoCrudRepository.save(producto));
Solución 02: En mi caso, en ProductMapper en esta linea se hace la traducción: (considerando @InheritInverseConfiguration)
Al realizar la conversión de product a producto siento productId del tipo int se asigna cero y por eso cuando se prueba desde postman sin usar el atributo productId al generar el objeto producto este viene con ese valor 0 y por ende el error 500.
En mi caso lo que hice fue cambiar el tipo de dato de int a Integer en la clase:
public class Product {private Integer productId;
Al probarlo, observe que como objeto viene con null y normal realiza el guardado del registro.
Espero le sea útil
Nota: Lo curioso he revisado la clase y el archivo del profesor pero ahi no hacen ningun cambio del int o la validación con el cambio de dato como en la solución 1.
Gracias... justo tenia el mismo error y no queria modificar el metodo. Saludos
Gracias Jaime, usé la solución tuya y me sirvió, advierto que también hay que cambiar el tipo de dato del set y get a Integer para que funcione correctamente.
Las rutas me dan 404, aunque tengo todo aparentemente bien configurado, incluso descargando el proyecto desde acá me dan 404. He revisado el context y está correcto. Que podría ser?
Yo tengo el mismo problema, he verificado todas las rutas y estan bien, pero aun así para cada solicitud http me sale 404 no found.
yo estoy en las mismas. Alguno pudo dar con la solucion?
Hola a todos!
Por favor podrían ayudarme a entender porque al final de la petición POST (min 7:42) category está en null.
Hola, tengo las misma interrogante, nos podría orientar profesor Alejandro Ramirez?
Por qué cuando se realiza el save, en la respuesta la relación(category) no viene cargada sino viene como null? No me quedó claro esa parte, gracias.
Saludos gracias por el curso. Tengo una problema al ejecutar save en postman me da error. Anexo imagen para ver si alguien puede ayudarme.
Action:
Consider defining a bean of type 'com.platzi.market.persistence.mapper.ProductMapper' in your configuration.
No te preocupes WIlliam. Estoy 99% seguro de que puede ser por una de estas dos razones:
ProductMapper no tiene la anotación con el componentModel @Mapper(componentModel = "spring", uses = {CategoryMapper.class})
La clase PlatziMarketApplication no está en el paquete com.platzi.market
Revisa esto y si no consigues hacerlo funcionar me compartes la URL de tu repositorio para ver el problema!
Tengo el mismo error, y he revisado ProductMapper y si le he colocado la anotación.
@Mapper(componentModel ="spring", uses ={CategoryMapper.class})publicinterfaceProductMapper{ @Mappings({ @Mapping(source ="idProducto", target ="productId"), @Mapping(source ="nombre", target ="name"), @Mapping(source ="idCategoria", target ="categoryId"), @Mapping(source ="precioVenta", target ="price"), @Mapping(source ="cantidadStock", target ="stock"), @Mapping(source ="estado", target ="active"), @Mapping(source ="categoria", target ="category")})```
Y la clase MarketApplication si se encuentra en el paquete correspondiente.Gracias por la ayuda.
De a poco y con paciencia, voy aprendiendo y avanzando en el curso. Me gusta mucho Spring. Algunos errores los puedo solucionar y en otros, me salva el genio de Alejandro. En este vídeo quedé en la parte del getAll(), en la primer petición digamos. En intellij IDEA no me arroja error ni nada, el problema es cuando pongo el sitio web tal cual como se ve en el vídeo, me arroja este error .
Ummm, ese "Not Found" es porque no estás llamando correctamente la url del API. En el archivo application.properties si tienes configurado el contexto con platzi-market o tienes otro nombre?
Puedo compartirte el proyecto en github?
Genial!!!
x2
Que emoción ver que todo funciona a la perfección :D
x2
Encontré un error que me sorprende que no aparezca en el video.
Si el tipo del id de Product es int primitivo y ningun id es pasado en el json el valor por defecto del id va a ser 0
No sé si es por mi versión de Java, estoy corriendo este proyecto con Java 21 y tener el código así me da una excepción org.hibernate.StaleObjectStateException ya que está intentando guardar el producto con id 0 enves de crear uno nuevo.
Se puede arreglar usando Integer envés de int. Es mejor práctica no usar tipos primitivos para poder aceptar valores nulos, de lo contrario siempre van a usar el valor por defecto que asigna Java.
Quizas alguien tuvo un error 401 en la peticion (platzi-market/api/products/all), hago el consumo desde postman y salta esto, lo hago desde el browser y me salta un formulario para poner user y contraseña...
Si se debe a que en el archivo de configuracion properties la propiedad del Context path no esta escrita correctamente, ya ha pasado que a veces lo escribimos como server.servlet.context-path pero la forma correcta es server.servlet.context.path
He tenido que agregar esto a la anotation SpringBootApplication ya que de lo contrario me daba este error
tuve el mismo error trabajando desde VS Code, el scaner funciona para el mapper, pero al usar postman me retorna un 404! :c
Supiste solucionarlo?
La Clase ProductoRepository debe tener la anotación @Repository
Hola, me enfrente a dos problemas. El primero al momento de querer guardar el producto en el método save me arrojaba el error 405 method not allowed. Una vez solucionado me arrojaba el error 415 Unsupported Media Type.
Para solucionar el primer error, observe que a la URL no le había quitado la palabra category.
Con error: platzi-market/api/products/category/save
Sin error: platzi-market/api/products/save
Para resolver el error 415, en el Postman agrege el siguiente header KEY Content-Type VALUE application/json dentro del menú Heades.