...
Ventajas: rápido, status code parametrizable.
Desventajas: Modelo de respuesta fijo.
Ejemplo de uso: Se captura una excepción en nuestro controlador Rest y se lanza una nueva ResponseStatusException con los valores deseados.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
try{
.....
throw new TestException( HttpStatus.BAD_REQUEST,"2. ResponseStatusException con FundeWebJSErrorAttributtes" );
.....
} catch ( final TestException e2 ) {
//Se captura una excepción existente y se lanza una nueva ResponseStatusException
log.error( "Test de prueba de ResponseStatusException con custom FundeWebJSErrorAttributtes: {}",e2.getMessage() );
throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, e2.getMessage(),e2.getCause() );
} .... |
...
La excepción ResponseStatusException es una evolución de otro mecanismo, la notación @ResponseStatus .
Por comodidad se recomienda usar ResponseStatusException, pero es posible seguir usando esta notación declarándola nuestras excepciones y lanzándolas desde nuestra API REST directamente.
Ver: Ejemplo ResponseStatus (https://gitlab.um.es/mncs/fundewebjsshowcase-api/-/blob/desarrollo/src/main/java/es/um/atica/fundewebjsshowcasebackend/exceptions/TestResponseStatusException.java).
Ejemplo ejecución:de uso: Se anota nuestra excepción con ResponseStatus y se lanza desde nuestro controlador Rest.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
...
@ResponseStatus( code = HttpStatus.BAD_REQUEST, reason = "Reason en ResponseStatus", value = HttpStatus.INTERNAL_SERVER_ERROR )
public class TestResponseStatusException extends RuntimeException
....
/
try{
...
throw new TestResponseStatusException( HttpStatus.BAD_REQUEST, "1. Notación ResponseStatus con FundeWebJSErrorAttributtes" );
...
} catch ( final TestResponseStatusException e1 ) {
log.error( "Test de prueba de notación @ResponseStatus con custom FundeWebJSErrorAttributtes: {}",e1.getMessage() );
throw e1;
}
... |
...
Ejemplo de fichero extendido: Se crea una clase nueva que extiende DefaultErrorAttributes, se anota con Component y se sobreescribe el método getErrorAttributes como se desee.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
@Component
public class FundeWebJSErrorAttributes extends DefaultErrorAttributes {
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager
.getLogger( FundeWebJSErrorAttributes.class );
public static final String TIPO_RESPUESTA = "CUSTOM_DefaultErrorAttributes";
@Override
public Map<String, Object> getErrorAttributes( WebRequest webRequest, boolean includeStackTrace ) {
final Map<String, Object> allErrorAttributes = super.getErrorAttributes( webRequest, includeStackTrace );
final Map<String, Object> errorAttributes = new HashMap<>();
final int statusCode = ( int ) allErrorAttributes.get( "status" );
errorAttributes.put( "tipoRespuesta", TIPO_RESPUESTA );
errorAttributes.put( "estado", HttpStatus.valueOf( statusCode ) );
errorAttributes.put( "fecha",
LocalDateTime.now().format( DateTimeFormatter.ofPattern( "dd/MM/yyyy HH:mm:ss" ) ) );
String mensaje = "";
final Throwable throwable = getError( webRequest );
if ( throwable instanceof ResponseStatusException ) {
final ResponseStatusException responseStatusException = ( ResponseStatusException ) throwable;
mensaje = responseStatusException.getReason() == null ? "" : responseStatusException.getReason();
} else if ( throwable instanceof ConstraintViolationException ) {
webRequest.setAttribute( "status", HttpStatus.BAD_REQUEST.value(), 1 );
errorAttributes.put( "estado", HttpStatus.BAD_REQUEST.toString() );
mensaje = throwable.getMessage() == null ? "" : throwable.getMessage();
} else {
if ( ( throwable != null ) && ( throwable.getMessage() != null ) ) {
mensaje = throwable.getMessage() == null ? "" : throwable.getMessage();
}
}
errorAttributes.put( "mensaje", mensaje );
return errorAttributes;
}
}
|
...
3. ExceptionHandler
En los métodos anteriores se ha explicado cómo lanzar excepciones en nuestros servicios y modificar el modelo de respuesta.
A partir de este punto se explicará cómo maneja Spring las excepciones y cómo modificar este mecanismo, lo que .
Esto permitirá gestionar las respuestas, definir modelos de respuesta diferentes dependiendo de cada excepción, y sobreescribir el comportamiento estándar en situaciones de error no controladas.
Como primera aproximación a este enfoque tenemos la notación @ExceptionHandler.
Ventajas: Permite manejar una excepción y modificar los valores de , modificando la respuesta http así como el modelo.
Desventajas: solo funciona de forma local en el controlador REST donde se haya definido.
Ejemplo de uso: Para probarlo simplemente se Se define un POJO con los datos para el body de la respuesta, se crea el ; en nuestro controlador Rest se crea método anotado con ExceptionHandler; y se lanza la excepción a capturarmanejar.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
@Data
@RequiredArgsConstructor
@AllArgsConstructor
@Builder
public class TestLocalHandlerError {
@NonNull
private HttpStatus estado;
@JsonFormat(shape = Shape.STRING, pattern = "dd/MM/yyyy hh:mm:ss")
private LocalDateTime fecha;
@NonNull
private String mensaje;
@Builder.Default
private String tipoRespuesta = "CUSTOM_LocalHandler";
}
|
...
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
{
"estado": "INTERNAL_SERVER_ERROR",
"fecha": "05/10/2021 12:37:46",
"mensaje": "3. Excepción con handler local y modelo custom TestLocalHandlerError",
"tipoRespuesta": "CUSTOM_LocalHandler"
} |
...
4. ControllerAdvice
El principal inconveniente del mecanismo anterior es que, si queremos manejar la misma excepción en varios controllers, habría que escribir el mismo método en cada uno de ellosellos.
La forma de manejar todas las expeciones de forma global y conjunta en toda nuestra aplicación se realiza a través de la notación @RestControllerAdvice.
| Info |
|---|
La notación RestControllerAdvice engloba las notaciones ControllerAdvice (que registra la clase en el contexto de Spring para que pueda acceder a todos los controllers) y ResponseBody (para que los pojos enviados en la respuesta se interpreten como JSON). |
...
| Info |
|---|
Es posible declarar varios RestControllerAdvice para dividir el manejo de excepciones por paquetes, clases, anotaciones, etc. |
Ejemplo de uso: Se crean tanto la excepción a capturar como el POJO con el modelo de respuesta; se crea una clase anotada con RestControllerAdvice y, dentro de ella, se crea el método anotado con ExceptionHandler para manejar nuestra excepción.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
public class TestControllerAdviceException extends RuntimeException {
private static final long serialVersionUID = 1L;
public TestControllerAdviceException( String msg ) {
super( msg );
}
} |
...
La opción recomendada para manejar esta situación es añadir un ExceptionHandler en nuestro ControllerAdvice (ver ejemplo 4).
Ejemplo de uso: En nuestro controlador Rest, se emplean las anotaciones de Bean Validation que se deseen (ver enlace en referencias); se crea el POJO para el modelo de respuesta que incluya una lista de errores de validación; en nuestra clase ControllerAdvice, se crea el método para manejar las excepciones javax.validation.ValidationException y se construye la respuesta.
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
/**
* Anotaciones Min y Max para validar el párametro Path de entrada "ex".
*/
@Override
public ResponseEntity<Object> handleError( HttpServletRequest request, HttpSession session,
@PathVariable( required = true ) @Min( 1 ) @Max( 4 ) Integer ex ) {...
} |
...
- https://wiki.um.es/wikis/programador/doku.php?id=fdw2.0:fundeweb2.0:gt:rest:manejo_errores_rest
- https://www.baeldung.com/spring-response-status-exception
- https://www.baeldung.com/exception-handling-for-rest-with-spring
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.html
- https://www.baeldung.com/problem-spring-web
- https://wiki.um.es/wikis/programador/doku.php?id=fdw2.0:fundeweb2.0:gt:rest:guia_validacion_servicio_rest
- https://reflectoring.io/bean-validation-with-spring-boot/
| Contenido por etiqueta | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
...