Capítulo 2: Anotações Web e REST
1. Anotações de Controller
@RestController vs @Controller
// Retorna VIEWS (HTML)
@Controller
public class PageController {
@GetMapping("/home")
public String home() {
return "home"; // Procura um template home.html
}
}
// Retorna DADOS (JSON/XML)
@RestController
public class ApiController {
@GetMapping("/api/data")
public Map<String, String> getData() {
return Map.of("message", "Hello"); // Serializado em JSON automaticamente
}
}
@ResponseBody
Força o retorno em JSON/XML (usado com @Controller).
@Controller
public class MixedController {
@GetMapping("/page")
public String page() {
return "page"; // Retorna uma view
}
@GetMapping("/api/info")
@ResponseBody
public Map<String, String> info() {
return Map.of("version", "1.0"); // Retorna JSON
}
}
2. Anotações de Mapping HTTP
@RequestMapping
Anotação básica para mapear uma URL para um método. Pode especificar o método HTTP.
@RestController
@RequestMapping("/api/v1/products") // Prefixo para todos os métodos
public class ProductController {
@RequestMapping(method = RequestMethod.GET)
public List<Product> getAll() { ... }
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Product getById(@PathVariable Long id) { ... }
}
@GetMapping
Atalho para @RequestMapping(method = GET).
@GetMapping
public List<Product> getAll() {
return productService.findAll();
}
@GetMapping("/{id}")
public Product getById(@PathVariable Long id) {
return productService.findById(id);
}
@GetMapping("/search")
public List<Product> search(@RequestParam String name) {
return productService.findByName(name);
}
@PostMapping
Atalho para @RequestMapping(method = POST).
@PostMapping
public ResponseEntity<Product> create(@Valid @RequestBody ProductDto dto) {
Product created = productService.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping
Atalho para @RequestMapping(method = PUT). Substituição completa de um recurso.
@PutMapping("/{id}")
public Product update(@PathVariable Long id, @Valid @RequestBody ProductDto dto) {
return productService.update(id, dto);
}
@PatchMapping
Atalho para @RequestMapping(method = PATCH). Atualização parcial de um recurso.
@PatchMapping("/{id}")
public Product partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
return productService.partialUpdate(id, updates);
}
@DeleteMapping
Atalho para @RequestMapping(method = DELETE).
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
productService.delete(id);
return ResponseEntity.noContent().build();
}
Resumo dos mappings
| Anotação | Método HTTP | Uso típico |
|---|---|---|
@GetMapping | GET | Ler dados |
@PostMapping | POST | Criar um recurso |
@PutMapping | PUT | Substituir um recurso |
@PatchMapping | PATCH | Modificar parcialmente |
@DeleteMapping | DELETE | Remover um recurso |
3. Anotações de Parâmetros
@PathVariable
Extrai um valor da URL.
// GET /api/users/42
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
// GET /api/users/42/orders/7
@GetMapping("/api/users/{userId}/orders/{orderId}")
public Order getOrder(@PathVariable Long userId, @PathVariable Long orderId) {
return orderService.findByUserAndId(userId, orderId);
}
// Nome diferente do parâmetro da URL
@GetMapping("/api/categories/{category-name}")
public Category getCategory(@PathVariable("category-name") String categoryName) {
return categoryService.findByName(categoryName);
}
@RequestParam
Extrai um parâmetro da query string.
// GET /api/products?category=electronics
@GetMapping("/api/products")
public List<Product> getByCategory(@RequestParam String category) {
return productService.findByCategory(category);
}
// Parâmetro opcional com valor padrão
// GET /api/products?page=0&size=20&sort=name
@GetMapping("/api/products")
public Page<Product> getAll(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "name") String sort) {
return productService.findAll(PageRequest.of(page, size, Sort.by(sort)));
}
// Parâmetro opcional sem valor padrão
@GetMapping("/api/search")
public List<Product> search(
@RequestParam(required = false) String name,
@RequestParam(required = false) BigDecimal minPrice) {
return productService.search(name, minPrice);
}
@RequestBody
Desserializa o corpo da requisição (JSON → Objeto Java).
@PostMapping("/api/users")
public User createUser(@RequestBody UserDto userDto) {
return userService.create(userDto);
}
// Corpo da requisição (JSON)
{
"name": "Jean Dupont",
"email": "jean@example.com",
"password": "secret123"
}
@RequestHeader
Extrai um valor de um header HTTP.
@GetMapping("/api/profile")
public User getProfile(@RequestHeader("Authorization") String authHeader) {
String token = authHeader.replace("Bearer ", "");
return userService.findByToken(token);
}
@GetMapping("/api/info")
public Map<String, String> info(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value = "Accept-Language", defaultValue = "fr") String lang) {
return Map.of("userAgent", userAgent, "language", lang);
}
@CookieValue
Extrai um valor de um cookie.
@GetMapping("/api/preferences")
public Preferences getPrefs(@CookieValue(value = "theme", defaultValue = "light") String theme) {
return new Preferences(theme);
}
@ModelAttribute
Vincula parâmetros de formulário a um objeto (principalmente usado com @Controller).
@PostMapping("/register")
public String register(@ModelAttribute RegistrationForm form) {
userService.register(form);
return "redirect:/login";
}
4. Anotações de Resposta
@ResponseStatus
Define o código HTTP da resposta.
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 201
public Product create(@RequestBody ProductDto dto) {
return productService.create(dto);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT) // 204
public void delete(@PathVariable Long id) {
productService.delete(id);
}
Usar com exceções:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
5. Anotações de Tratamento de Erros
@ExceptionHandler
Trata exceções no nível do controller.
@RestController
public class UserController {
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // Pode lançar ResourceNotFoundException
}
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String, String> handleNotFound(ResourceNotFoundException ex) {
return Map.of("error", ex.getMessage());
}
}
@ControllerAdvice / @RestControllerAdvice
Tratamento global de exceções para todos os controllers.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse("NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.toList();
return new ErrorResponse("VALIDATION_ERROR", errors.toString());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGeneral(Exception ex) {
return new ErrorResponse("INTERNAL_ERROR", "Ocorreu um erro");
}
}
6. Anotações CORS e Outras
@CrossOrigin
Configura o Cross-Origin Resource Sharing (CORS).
@RestController
@CrossOrigin(origins = "http://localhost:3000")
public class ApiController {
// Todos os métodos permitem requisições de localhost:3000
}
@GetMapping("/api/public")
@CrossOrigin(origins = "*", maxAge = 3600)
public String publicEndpoint() {
return "acessível de qualquer lugar";
}
7. Anotações de Validação
@Valid / @Validated
Ativa a validação dos dados recebidos.
@PostMapping("/api/users")
public User create(@Valid @RequestBody CreateUserDto dto) {
return userService.create(dto);
}
public class CreateUserDto {
@NotNull(message = "O nome é obrigatório")
@Size(min = 2, max = 50, message = "O nome deve ter entre 2 e 50 caracteres")
private String name;
@NotBlank(message = "O e-mail é obrigatório")
@Email(message = "Formato de e-mail inválido")
private String email;
@NotNull
@Size(min = 8, message = "A senha deve ter pelo menos 8 caracteres")
private String password;
@Min(value = 0, message = "A idade deve ser positiva")
@Max(value = 150, message = "A idade deve ser menor que 150")
private Integer age;
@Pattern(regexp = "^\\+?[0-9]{10,15}$", message = "Número de telefone inválido")
private String phone;
}
Anotações de validação disponíveis
| Anotação | Descrição |
|---|---|
@NotNull | Não pode ser null |
@NotEmpty | Não null e não vazio (String, Collection) |
@NotBlank | Não null, não vazio, não só espaços |
@Size(min, max) | Tamanho entre min e max |
@Min(value) | Valor mínimo (números) |
@Max(value) | Valor máximo (números) |
@Email | Formato de e-mail válido |
@Pattern(regexp) | Corresponde à expressão regular |
@Positive | Número estritamente positivo |
@PositiveOrZero | Número positivo ou zero |
@Negative | Número estritamente negativo |
@Past | Data no passado |
@Future | Data no futuro |
@DecimalMin | Valor decimal mínimo |
@DecimalMax | Valor decimal máximo |
Resumo Rápido
| Anotação | Papel |
|---|---|
@RestController | Controller REST (JSON) |
@RequestMapping | Mapping de URL base |
@GetMapping | GET |
@PostMapping | POST |
@PutMapping | PUT |
@PatchMapping | PATCH |
@DeleteMapping | DELETE |
@PathVariable | Parâmetro na URL |
@RequestParam | Parâmetro query string |
@RequestBody | Corpo JSON → Objeto |
@RequestHeader | Header HTTP |
@ResponseStatus | Código de resposta HTTP |
@ExceptionHandler | Tratamento de exceção local |
@RestControllerAdvice | Tratamento de exceção global |
@CrossOrigin | Configuração CORS |
@Valid | Ativa validação |
➡️ Capítulo 3: Anotações Data e JPA