You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

0 Días
9 Hrs
57 Min
16 Seg
Curso de Java Spring

Curso de Java Spring

Alejandro Ramírez

Alejandro Ramírez

Controlar las respuestas HTTP

26/35
Resources

How to improve endpoints with ResponseEntity in Spring?

To talk about an effective API controller is to talk about the proper management of HTTP responses. Implementing ResponseEntity in Spring not only improves the readability of the code, but also the clarity and robustness of the API in question. How to control the calls and improve the response of our endpoints? Read on to learn how to implement these transformations successfully.

What is ResponseEntity in Spring?

ResponseEntity is a class in Spring that allows to manage in a more controlled way the responses of the endpoints of an API. This control is fundamental to define the body and status code of an HTTP response, giving more clarity to users about how their request was handled.

  • Response Control: Using ResponseEntity, you can indicate exactly what is being returned and with what status code (such as 200 OK, 201 Created or 404 Not Found).
  • Error Handling: Allows you to define what to do when an operation does not work as expected, improving communication with the client.

How to use ResponseEntity with getAll?

To improve our getAll responses, we will start by replacing the return of a simple list with a ResponseEntity. This way we control not only the content, but also the HTTP response code.

List<Product> products = productRepository.findAll();return new ResponseEntity<>(products, HttpStatus.OK);

How to handle an optional with ResponseEntity?

An Optional represents the possibility that a product exists, or not, in the database. It can be transformed to respond appropriately according to the result.

return productService.getProduct(id).map(product -> new ResponseEntity<>(product, HttpStatus.OK)).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));

How to implement ResponseEntity in Get by Category?

To get products by category, the same logic applies: instead of returning empty or null lists, respond with ResponseEntity and the appropriate code.

List<Product> productsByCategory = productService.getByCategory(categoryId);return new ResponseEntity<>(!productsByCategory.isEmpty() ? productsByCategory : HttpStatus.NOT_FOUND);

What improvements does ResponseEntity bring to the Save method?

The save method can correctly reflect when a product was successfully created using the 201 Created status code.

Product product = productService.save(newProduct);return new ResponseEntity<>(product, HttpStatus.CREATED);

How to handle the delete efficiently?

In the case of the delete method, ResponseEntity helps us to control whether a delete was successful or not, depending on the previous existence of the product.

boolean deleted = productService.delete(id);return new ResponseEntity<>(deleted ? HttpStatus.OK : HttpStatus.NOT_FOUND);

How to verify the changes in Postman?

After applying these enhancements, it is crucial to verify the correct operation of the endpoints. Postman is an excellent tool for testing to ensure that each endpoint returns the expected status and body.

Example Test:

  1. Non-existing product: Query a product that does not exist, it should now respond with 404 Not Found.
  2. Product Creation: Create a product and verify that the response now includes 201 Created, reflecting that the resource was successfully created.

Spring, along with the proper use of ResponseEntity, allows building more robust and clear APIs, thus improving the developer and end-user experience. Don't stop here, keep improving your services and exploring new features for your projects.

Contributions 26

Questions 14

Sort by:

Want to see more contributions, questions and answers from the community?

Desde la versión 5 de Spring existe el siguiente método static en ResponseEntity.

ResponseEntity.of(productService.getProduct(productId))

Uno puede usar la anterior línea como alternativa a las líneas 26, 27 y 28.

En el método delete, si no quieren usar if y else, también pueden usar el operador ternario:

@DeleteMapping("/delete/{id}")
public ResponseEntity delete(@PathVariable("id") int productId) {
    return new ResponseEntity(this.productService.delete(productId)
            ? HttpStatus.OK
            : HttpStatus.NOT_FOUND);
}

Una observacion, cuando se consulta a la base de datos y se desea obtener una lista de producto por categoria y la categoria no existe entonces se devuelve una lista vacía, entonces, al hacer el map or else nunca entra al or else porque En el Optional si está presenta la lista, solo que está vacía, entonces aunque la categoria no exista siempre devolverá estatus OK -> 200.

Yo lo hice asi:

    @GetMapping("/category/{id}")
    public ResponseEntity<List<Product>> getByCategory(@PathVariable("id") int categoryId){
    	
    	List<Product> products = productService.getByCategory(categoryId).orElse(null);
    	
        return products != null && !products.isEmpty() ?
        				new ResponseEntity<>(products, HttpStatus.OK)
        				: new ResponseEntity<List<Product>>(HttpStatus.NOT_FOUND);
    }

porque un not found en vez de un no content???

Me funcionan todos los métodos excepto el getByCategory, cuando no encuentra los productos de una categoría retorna Status 200 OK.
Aquí dejo el código:

	@GetMapping("/category/{categoryId}")
	public ResponseEntity<List<Product>> getByCategory(@PathVariable("categoryId") int categoryId){
		return productService.getByCategory(categoryId)
				.map(products -> new ResponseEntity<>(products,HttpStatus.OK))
				.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
	}```

Esta fue mi forma para hacer el delete, por si a alguien le ayuda

@DeleteMapping("/{productId}")
    public ResponseEntity<Void> delete(@PathVariable int productId) {
        boolean deleted = productService.delete(productId);
        return deleted ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
    }

Justo hice una consulta en la clase anterior, pero después de terminar la clase me parece que tiene mucho sentido diferenciar la creación de la actualización del producto.

 @PutMapping()
    public ResponseEntity<Product> create(@RequestBody Product product) {
        return new ResponseEntity<>(productService.save(product), HttpStatus.CREATED);
    }

    @PostMapping()
    public ResponseEntity<Product> update(@RequestBody Product product) {
        return new ResponseEntity<>(productService.save(product), HttpStatus.OK);
    }

ResponseEntity esta muy completo y puedes usarlo de varias formas, explora escribiendo ResponseEntity. + ctrl

return ResponseEntity.of(Optional.ofNullable(productService.getAll()));
        
return ResponseEntity.ok().body(productService.getAll());
        
return ResponseEntity.status(HttpStatus.OK).body(productService.getAll());
        
return new ResponseEntity<>(productService.getAll(), HttpStatus.OK);

En lo personal me gusta más esta ya que te responde un HttpStatus y el body

return ResponseEntity.status(HttpStatus.OK).body(productService.getAll());
@GetMapping no debe responder con NOT\_FOUD ya que es confuso, el cliente puede pensar que /{id} no existe y está llamando al endpoint incorrecto, para esto está NO\_CONTENT que indica que si se encuentra el recurso /{id} pero no hay un producto con el id proporcionado

Es una de las mejores clases porque empiezo en Spring, anteriormente hice un proyecto básico, pero no incluía las ResponseEntity, si que son muy utiles!

Una forma un poco más elegante de definir el delete:

@DeleteMapping("/{id}")
public ResponseEntity delete(@PathVariable("id") int productId) {
    return productService.delete(productId) ?
            new ResponseEntity<>(HttpStatus.NO_CONTENT) :
            new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

¿Alguien ha notado que cuando se hace un request que devuelve una lista y esta no tiene elementos, igual devuelve status 200?

Por ejemplo si haces un request a /products/category/583 (la categoría 583 no existe) igual devuelve status 200 y debería devolver 404

Las peticiones en spring cómo funcionan? cada petición es única y se atiende de forma personalizada, pero Spring puede atender múltiples de peticiones únicas al mismo tiempo ?, espero pueda darme a entender

Caused by: org.postgresql.util.PSQLException: ERROR: column c1\_0.id\_categoria does not exist@Entity @Table(name="categorias") public class Categoria { Si llegan a tener este error, revisen sus entidades en la carpeta entity y chequen que si le esten poniendo el nombre correcto de la tabla de la base de datos. ```js @Entity @Table(name="categorias") // aqui esta el problema public class Categoria {} ```
* ` @GetMapping("/{id}") public ResponseEntity getProducto(@PathVariable Long id){ Map<String,Object> data = new HashMap<>(); if(productoService.getProduct(id).isPresent()){ data.put("data", productoService.getProduct(id)); return ResponseEntity.ok(data); } return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No se encontro el id"); }`
@GetMapping("/{id}") public ResponseEntity\ getProducto(@PathVariable Long id){ Map\<String,Object> data = new HashMap<>(); if(productoService.getProduct(id).isPresent()){ data.put("data", productoService.getProduct(id)); return ResponseEntity.ok(data); } return ResponseEntity.status(HttpStatus.NOT\_FOUND).body("No se encontro el id"); }

El delete:

 @DeleteMapping("/{productId}")
    public ResponseEntity delete(@PathVariable int productId){
        return productService.delete(productId)
                ? new ResponseEntity(HttpStatus.OK)
                : new ResponseEntity(HttpStatus.NOT_FOUND);
    }

Me parece interesante ver cómo se va construyendo todo. Comienza con unos pocos elementos y ya llega hasta acá.

les envio el de lista de productos por categoria con lambda
return new ResponseEntity<>(productService.getByCategory(categoryId).orElseGet(ArrayList::new), HttpStatus.OK);

les envio el de getProduct con lambda
@GetMapping("/{productId}")
public ResponseEntity<Product> getProduct(@PathVariable(“productId”) BigDecimal productId) {
return new ResponseEntity<>(productService.getProduct(productId).orElseGet(Product::new), HttpStatus.OK);
}

Si la funcion getByCategory en Postman devuelve una lista vacia puede ser porque (al igual que yo) tenias un codigo en el repository tal que:

@Override
    public Optional<List<Product>> getByCategory(int categoryId) {
        List<Producto> productos = productoCrudRepository.findByIdCategoriaOrderByNombreAsc(categoryId);
        return Optional.of(mapper.toProducts(productos));
    }

Lo que hace el Optional.of es que crea un Optional con una lista y si no hay productos será una lista vacía no un Optional vacío que es lo que buscamos, para eso se podría hacer con Optional.empty(), dejando el codigo asi:

@Override
public Optional<List<Product>> getByCategory(int categoryId) {
List<Producto> productos = productoCrudRepository.findByIdCategoriaOrderByNombreAsc(categoryId);
if (productos.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(mapper.toProducts(productos));
}
}

Ahora si devuelve un error 404 not found

Podemos usar operadores condicionales ternarios en el delete

@DeleteMapping("{id}")
    public ResponseEntity delete (@PathVariable("id") int productId) {
        return productService.delete(productId) ? 
								new ResponseEntity(HttpStatus.OK) :
								new ResponseEntity(HttpStatus.NOT_FOUND);
    }```

De esta manera puede quedar algo mas limpio el delete.

@DeleteMapping("/delete/{id}")
    public ResponseEntity delete(@PathVariable("id") int id){
        ResponseEntity response = productService.delete(id) ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
        return  response;
    }

Para el endpoint DELETE se puede evitar el condicional ELSE.

Si se cumple la condición, se retornará OK, si no se cumple, se retornará NOT_FOUND sin problemas.

if (productService.delete(productId)) return new ResponseEntity<>(HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);

Yo cree el metodo para actualizar pero aún no estoy muy convencido de si esta bien. Me podrían dar feedback

@PutMapping(value = "/update/{productId}")
public ResponseEntity<Product> update(@PathVariable("productId") long productId, @RequestBody Product product){
    if (productService.getProduct(productId) == null){
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }else{
        return new ResponseEntity<>(productService.update(product), HttpStatus.OK);
    }
}