【问题标题】:Spring boot exception handling best practiceSpring Boot 异常处理最佳实践
【发布时间】:2022-05-10 16:13:31
【问题描述】:

我有一个关于异常处理的简单问题。 我目前有一个应用分为多个层:控制器、服务、存储库,我的问题是:异常处理应该由控制器还是服务完成?

例子:

控制器:

@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);
}

服务:

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 !");
    }

}

最佳做法是什么,我应该从控制器中删除我的 try-catch 并将其放入我的服务中吗?因为使用此代码,我的 404 异常被控制器捕获覆盖。

【问题讨论】:

  • 您的意思是使用像@ControllerAdvice 这样的异常处理程序的最佳实践,还是只想知道一般捕获处理的最佳实践?
  • 要捕获,我的控制器应该调用我的服务还是应该在里面尝试/捕获

标签: spring spring-boot


【解决方案1】:

如果你想抛出异常,把它扔到服务层。

最好再定义一个名为异常的包。

并在其中包含您的自定义异常、异常响应和异常处理程序。

我的代码结构如下:

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);
    }
}

希望你能得到答案。

【讨论】:

    【解决方案2】:

    是的,您需要转为服务。您可以通过创建扩展 ResponseEntityExceptionHandler 的类来处理异常

    【讨论】:

      【解决方案3】:

      如果你不使用@ControllerAdvice,当异常类型决定你想在客户端呈现什么时,最佳实践是从你的服务中抛出一些东西并专门在控制器上捕获它。因此,每个方法都会捕获每个特定的异常,并根据异常类型决定输出什么。例如:当它是空指针时输出错误500,当它缺少请求时输出错误400,等等。

      除非您的业务逻辑需要处理异常,否则它应该被您的用例服务捕获。

      但是,如果您使用 Spring(或任何具有异常处理程序的框架),我更喜欢使用 @ControllerAdvice,因为您不再需要捕获控制器上的所有内容,只需将 throws Exception 放在每个控制器方法上即可将赶上@ControllerAdvice 课程。因此,决定输出的任何逻辑都基于全局类(或者您可以将其设置为仅来自某些类或仅某些包的句柄),并且您的控制器方法将更短、更清晰并减少代码重复。

      【讨论】:

        【解决方案4】:

        考虑到您的 Spring Boot 应用程序具有控制器、服务和存储库层。

        您应该始终实现 ControllerAdvice 类。这将确保为您的客户正确处理错误。

        服务层应该能够捕获和处理所有检查异常。这些异常是可恢复的。另一方面,RuntimeException 是一个严重错误,应该停止进程。

        简单用例:客户端通过 http 向服务请求信息。

        存储库访问数据库,但遇到异常,因为数据库暂时不可用。 Repo 引发 RuntimeException。

        这个异常会被ControllerAdvice类捕获。它将记录错误并为客户端构建适当的 ResponseEntity。

        这很重要,因为 ResponseEntity 不应包含任何与您的应用程序架构相关的敏感信息。

        【讨论】:

          猜你喜欢
          • 2018-01-03
          • 1970-01-01
          • 2017-05-11
          • 2013-05-09
          • 2011-11-10
          • 1970-01-01
          • 2013-04-22
          相关资源
          最近更新 更多