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!
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.
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?
Buen día me podrían ayudar a mi me muestra el siguiente mensaje de error yo estoy trabajando con el ide Spring tool suite.
APPLICATION FAILED TO START
Description:
Field mapper in com.platzi.market.persistence.ProductoRepository required a bean of type 'com.platzi.market.persistence.mapper.ProductMapper' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.platzi.market.persistence.mapper.ProductMapper' in your configuration.
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?
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
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.
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)
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.
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.
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.