@ControllerAdvice e a forma mais limpa de centralizar o tratamento de erros em uma aplicacao Spring Boot. Em vez de espalhar try/catch por todos os controllers, voce trata tudo em um lugar so.
Crie uma classe com @ControllerAdvice:
@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException::class)
fun handleNotFound(ex: ResourceNotFoundException, request: WebRequest):
ResponseEntity<Map<String, Any>> {
val body = mapOf(
"error" to "Not Found",
"message" to ex.message,
"status" to 404
)
return ResponseEntity(body, HttpStatus.NOT_FOUND)
}
@ExceptionHandler(MethodArgumentNotValidException::class)
fun handleValidation(ex: MethodArgumentNotValidException):
ResponseEntity<Map<String, Any>> {
val erros = ex.bindingResult.fieldErrors.map {
"${it.field}: ${it.defaultMessage}"
}
val body = mapOf(
"error" to "Validation Failed",
"details" to erros,
"status" to 400
)
return ResponseEntity(body, HttpStatus.BAD_REQUEST)
}
}
Crie excecoes customizadas:
class ResourceNotFoundException(message: String) : RuntimeException(message)
class BusinessException(message: String) : RuntimeException(message)
Uso no controller:
@RestController
class UsuarioController(private val service: UsuarioService) {
@GetMapping("/usuarios/{id}")
fun buscar(@PathVariable id: Long): ResponseEntity<Usuario> {
val usuario = service.buscarPorId(id)
?: throw ResourceNotFoundException("Usuario $id nao encontrado")
return ResponseEntity.ok(usuario)
}
}
Quando o usuario nao existe, o ResourceNotFoundException e lancado. O GlobalExceptionHandler captura e retorna um JSON limpo.
Para validacao de entrada com @Valid:
data class CriarUsuarioRequest(
@field:NotBlank val nome: String,
@field:Email val email: String,
@field:Min(18) val idade: Int
)
@PostMapping("/usuarios")
fun criar(@Valid @RequestBody request: CriarUsuarioRequest): ResponseEntity<Usuario> {
val usuario = service.criar(request)
return ResponseEntity(usuario, HttpStatus.CREATED)
}
Se a validacao falha, o MethodArgumentNotValidException e lancado e o handler devolve os erros campo a campo.
Para excecoes genericas nao mapeadas:
@ExceptionHandler(Exception::class)
fun handleGeneric(ex: Exception): ResponseEntity<Map<String, Any>> {
val body = mapOf(
"error" to "Internal Server Error",
"message" to (ex.message ?: "Erro inesperado"),
"status" to 500
)
return ResponseEntity(body, HttpStatus.INTERNAL_SERVER_ERROR)
}
Monte a resposta padrao em uma data class:
data class ErrorResponse(
val error: String,
val message: String?,
val status: Int,
val timestamp: Long = System.currentTimeMillis()
)
@ExceptionHandler(BusinessException::class)
fun handleBusiness(ex: BusinessException): ResponseEntity<ErrorResponse> {
val body = ErrorResponse(
error = "Business Error",
message = ex.message,
status = 422
)
return ResponseEntity(body, HttpStatus.UNPROCESSABLE_ENTITY)
}
Para mais de um tipo de excecao no mesmo handler:
@ExceptionHandler(IOException::class, DataAccessException::class)
fun handleInfra(ex: Exception): ResponseEntity<ErrorResponse> {
return ResponseEntity(
ErrorResponse("Infra Error", ex.message, 503),
HttpStatus.SERVICE_UNAVAILABLE
)
}
Com @ControllerAdvice voce tem um unico ponto de tratamento de erros. O codigo dos controllers fica limpo e as respostas de erro seguem um padrao consistente.
That's all for now.
Thanks for reading!
Top comments (0)