Bastante nuevo en el reposapiés de primavera y tengo preguntas sobre cómo y la mejor manera de manejar las respuestas al cliente que usa el servicio. Actualmente, tengo el siguiente código de controlador para manejar el caso de que no se encuentre un registro en una consulta de base de datos:

@PreAuthorize("hasAuthority('ROLE_USER')")
@GetMapping("distance/{id}")
public ResponseEntity<?> getDistanceById(@PathVariable("id") Integer id) {
    log.info("getDistanceById");
    Distance distance = distanceService.getDistanceById(id);
    if (distance == null){
        return new ResponseEntity<CustomErrorMsg>(new CustomErrorMsg("Distance ID " + id + " Not Found"),HttpStatus.NOT_FOUND);
    }
    return new ResponseEntity<Distance>(distance, HttpStatus.OK);
}

Donde CustomErrorMsg es una clase simple que establece una cadena en su constructor.

¿Es este el mejor enfoque? ¿Una clase basada en ControllerAdvice sería un mejor enfoque? Haciendo esta pregunta porque el código anterior envía una respuesta 403 esperada cuando un cliente lo llama sin permiso. Me gustaría entender cómo está ocurriendo eso y si se puede usar para una condición NO ENCONTRADA.

0
geezer57 15 nov. 2017 a las 18:26

2 respuestas

La mejor respuesta

"Recurso no encontrado" es un caso de uso bien conocido, para el cual no tiene que "molestarse" con ResponseEntity o ControllerAdvice. Simplemente puede usar ResourceNotFoundException.

@PreAuthorize("hasRole('USER')")
@GetMapping("distance/{id}")
public Distance getDistanceById(@PathVariable("id") Integer id) {
    log.info("getDistanceById");
    Distance distance = distanceService.getDistanceById(id);
    if (distance == null) {
        throw new ResourceNotFoundException("Distance ID " + id + " Not Found");
    }

    return distance;
}

Declarar ResponseEntity<?> como tipo de retorno es correcto pero no transmite mucha información, ya que está colocando datos reales y un mensaje de error en el mismo nivel. Si, como yo, prefiere utilizar los constructores estáticos ResponseEntity, elija:

@PreAuthorize("hasRole('USER')")
@GetMapping("distance/{id}")
public ResponseEntity<Distance> getDistanceById(@PathVariable("id") Integer id) {
    log.info("getDistanceById");
    Distance distance = distanceService.getDistanceById(id);
    if (distance == null){
        throw new ResourceNotFoundException("Distance ID " + id + " Not Found");
    }

    return new ResponseEntity.ok(distance);
}

Nuevamente, lo que le interesa es una Distancia (su código probablemente se encuentra en una clase llamada DistanceController), así que no enfaticemos cuando no se encuentra.

Ahora, con respecto al estado HTTP. Si solicita /distance/<id> con privilegios insuficientes, obtendrá una denegación de acceso (403 Prohibido), que no es lo mismo que un recurso desconocido (404 No encontrado), es decir, por cierto, el estado devuelto cuando se lanza ResourceNotFoundException.

Aquí, los permisos para acceder a la URL solicitada se verifican primero. Si el usuario está autenticado con autoridades insuficientes, obtiene un error 403. No es así, puede irse y obtendrá el recurso solicitado (200), a menos que no exista (404).

0
Marc Tarin 15 nov. 2017 a las 16:30

Sugeriría usar la clase anotada RestControllerAdvice (por ejemplo, llamada GlobalExceptionHandler) para manejar el caso de error. Deberá cambiar el método getDistanceById para generar una excepción personalizada cuando la distancia sea nula. Deberá agregar un método al GlobalExceptionHandler que manejará su excepción personalizada. luego puede cambiar su código a lo siguiente:

@PreAuthorize("hasAuthority('ROLE_USER')")
@GetMapping("distance/{id}")
public ResponseEntity<Distance> getDistanceById(@PathVariable("id") Integer id) {
    log.info("getDistanceById");
    return ResponseEntity.ok(distanceService.getDistanceById(id));
}
0
haykart 15 nov. 2017 a las 16:26