Spring boot exception handling best practice
Asked Answered
S

5

8

I have a simple question about exception handling. I currently have an application divided into multiple layers: controller, service, repository, and my question is the following: the exception handling should be done by the controller or the service?

Example:

Controller:

@PostMapping(value = "/{id}/parents", produces = "application/json; charset=utf-8")
public ResponseEntity<AccommodationRequestDTO> resident(@PathVariable Long id, @Valid @RequestBody ParentsAndUrgencyContactDTO parentsAndUrgencyContactDTO) {
    AccommodationRequestDTO saved;
    try {
        saved = this.service.parents(id, parentsAndUrgencyContactDTO);
    } catch (Exception e) {
        throw new ResponseStatusException(
                HttpStatus.INTERNAL_SERVER_ERROR,
                "Failed to save request", e);
    }
    return ResponseEntity.ok(saved);
}

Service:

public AccommodationRequestDTO parents(Long id, ParentsAndUrgencyContactDTO parentsAndUrgencyContactDTO) {
    Optional<AccommodationRequest> accommodationRequest = repository.findById(id);

    if (accommodationRequest.isPresent()) {
        AccommodationRequest saved = accommodationRequest.get();
        Parent firstParent = parentMapper.toEntity(parentsAndUrgencyContactDTO.getFirstParent());
        Parent secondParent = parentMapper.toEntity(parentsAndUrgencyContactDTO.getSecondParent());

        firstParent = parentRepository.save(firstParent);
        secondParent = parentRepository.save(secondParent);

        saved.setFirstParent(firstParent);
        saved.setSecondParent(secondParent);
        saved = this.repository.save(saved);

        return mapper.toDTO(saved);
    } else {
        throw new ResponseStatusException(
                HttpStatus.NOT_FOUND, " with id " + id + " not found !");
    }

}

What is the best practice, should I remove my try-catch from the controller and put it in my service? Because with this code my 404 Exception is overridden by the controller catch.

Springclean answered 23/3, 2021 at 11:12 Comment(2)
do you mean best practice using Exception handler like @ControllerAdvice or do you just want to know the best practice to catch handling in general?Nevernever
To catch, should my controller just call my service or should I have try/catch insideSpringclean
C
8

If you want to throw a exception throw it in service layer.

It will be better to define one more package named exception.

and have your custom exception, exception response and exception handler in it.

My code structure looks like:

package com.***.demo.exception;

public class DataNotFound extends EntityNotFoundException {
    public DataNotFound(String message) {
        super(message);
    }
}
package com.***.demo.exception;

@Data
@AllArgsConstructor
public class ErrorDetails {
    private Date timestamp;
    private String message;
    private String details;
}
package com.***.demo.exception;

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(DataNotFound.class)
    public ResponseEntity<?> dataNotFoundExceptionHandling(Exception exception, WebRequest request) {
        return new ResponseEntity<>(new ErrorDetails(new Date(), exception.getMessage(), request.getDescription(false)), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> globalExceptionHandling(Exception exception, WebRequest request) {
        return new ResponseEntity<>(new ErrorDetails(new Date(), exception.getMessage(), request.getDescription(false)), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Hope you got your answer.

Chuvash answered 23/3, 2021 at 12:39 Comment(0)
O
1

Yes you need to move to service. You can handle exception by creation class that extends ResponseEntityExceptionHandler

Ossified answered 23/3, 2021 at 11:17 Comment(0)
S
1

Considering your spring boot application has a controller, service and repository layer.

You should always implement a ControllerAdvice class. This will ensure proper error handling for your clients.

The Service Layer should be able to catch and treat all Checked Exceptions. These exceptions are recoverable. A RuntimeException on the other hand, is a critical error and should stop the process.

Simple use-case: client requests information from service via http.

The repository accesses the database, but is confronted with an exception, because the db is not available at the moment. The Repo throws a RuntimeException.

This exception will be catched by the ControllerAdvice class. It will log the error and build an appropriate ResponseEntity for the client.

This is important, because the ResponseEntity should not contain any sensitive information concerning the architecture of your application.

Silkaline answered 24/2, 2022 at 14:36 Comment(2)
Good point, I am learning Spring Boot very thoroughly. I have a Service layer that catches all Database Exceptions, my only question is should i throw unchecked vs checked exceptions from the Service Layer ? My idea was the service Layer throws an exception that is caught by the Controller layer and then it can send an Http response but if its checked, i have to add try/catch in all my tests, which i dont likeHurling
@theMyth, there seemingly never is a one fits all answer in design. Some thoughts: Is the exception thrown by the db-layer a checked one? Is the process recoverable by exception handling in the service layer? If not, why catch it in the service layer? It would of course make sense to catch a checked one and rethrow a unchecked. I would favor throwing unchecked exceptions whenever possible, in order to not pollute the code. Reduce the passages of exception handling to a minimum. Therefore the ControllerAdvice is a great place to catch almost everything.Silkaline
N
0

if you don't use @ControllerAdvice, the best practice when the exception type decide what you want to present on client is throws something from your service and catch it specifically on controller. so, every method catch every specific exception and decide what the output depends on exception type. for example: when it's nullpointer the output is error 500, when it's missing request the output is error 400, etc.

unless the exception is something that you need to handle by your business logic, then it should be catch on your use case service.

But if you use Spring (or any frameworks which have Exception Handler) I prefer to use @ControllerAdvice as you don't need to catch every single things on controller anymore, just put throws Exception on every controller method and it will be catch on @ControllerAdvice class. so, any logic to decide the output is based on that class globally(or you can set it up to only handles from some classes only or some packages only) and your controller method will be shorter, cleaner and reduce code duplication.

Nevernever answered 23/3, 2021 at 12:59 Comment(0)
B
0
    1. define custom exception handling in your Spring Boot project

      • use @ExceptionHandler in class that annotated with
      • @Controller | @ControllerAdvice | @RestController | @RestControllerAdvice
        
    1. Throw any exception you want in the Service layer

    1. They will be caught automatically by AOP in Controllers or RestControllers

Note that with REST API best practices, it is not recommended to have logic in the controller layer, just focus on the Request and Response and do all the logic in the Service layer. Exception handling is also part of the business logic.

Bootle answered 19/5 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.