首页 > 解决方案 > 处理服务层结果的正确模式

问题描述

所以一段时间以来我一直在使用 Spring 和 Java 来构建微服务。我担心我目前处理使用“业务异常”的服务层结果的方式

控制器

@RestController
public class PurchaseController {
  @Autowired
  private PurchaseService purchaseService;

  @PostMapping("/checkout")
  public ResponseEntity<?> checkout(@RequestBody CheckoutRequest body) {
    try {
      SomeDTO dto = purchaseService.doCheckout(body);

      return ResponseEntity.ok(dto);
    }
    catch (UnauthorizedException e) {
      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
    }
    catch (CustomBusinessException e) {
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }
  }
}

服务

@Service
public class PurchaseService {
  // ...

  public DTO doCheckout(CheckoutRequest request) {
    // this one calls another microservice
    if (!isUserValid(request.userId)) {
      // current handling of business rules violation (1)
      throw new UnauthorizedException("User not valid");
    }

    if (request.total < 10) {
      // current handling of business rules violation (2)
      throw new CustomBusinessException("Minimum checkout at 20 dollars");
    }

    // ... do actual checkout
    return new DTO(someDTOData);
  }
}

我很喜欢使用这种“模式”,因为我不需要“如果”控制器级别的业务结果来返回适当的HttpStatusCode,但由于我发现一些文章说异常在 Java 中特别昂贵,我怀疑什么从长远来看,我所做的是好的。

是否有另一种正确的方式来优雅地处理业务结果层?

标签: javaspringspring-boot

解决方案


Spring 中 ResponseEntity 的问题在于,它们是在成功调用端点时使用您想要返回的结果对象键入的,因此您不能返回与快乐路径不同的另一个主体,在您的情况下是 SameDTO。解决此问题的一种方法是使用 ? 作为响应实体的类型,正如您所做的那样,但这不是最推荐的方式。

因此,最好的方法就是在无法返回预期对象并且必须返回另一个对象或状态码的情况下使用异常,而不是在控制器中使用 try-catch使用异常处理程序(控制器建议)https://www.baeldung.com/exception-handling-for-rest-with-spring

此控制器建议将捕获应用程序中引发的任何异常,并且根据异常类型,它可以返回不同的响应类或状态代码,而不会影响主控制器。如何成为您的控制器建议的一个示例是:

@ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {


  @ExceptionHandler(RuntimeException.class)
  public ResponseEntity<String> handleInternal(final RuntimeException ex) {
    return ResponseEntity
    .status(HttpStatus.INTERNAL_SERVER_ERROR)
    .body(ex.getMessage());
  }

  @ExceptionHandler(UnauthorizedException.class)
  public ResponseEntity<ResponseDto> identityClientException(UnauthorizedException e) {
    return ResponseEntity
    .status(HttpStatus.UNAUTHORIZED)
    .body(e.getMessage());
}
@ExceptionHandler(CustomBusinessException.class)
      public ResponseEntity<ResponseDto> identityClientException(CustomBusinessException e) {
        return ResponseEntity
        .status(HttpStatus.BAD_REQUEST)
        .body(e.getMessage());
}

如果没有异常处理逻辑,您的控制器会更加干净:

@RestController
public class PurchaseController {
  @Autowired
  private PurchaseService purchaseService;

  @PostMapping("/checkout")
  public ResponseEntity<SomeDTO> checkout(@RequestBody CheckoutRequest body){
      SomeDTO dto = purchaseService.doCheckout(body);
      return ResponseEntity.ok(dto);
  }
}

推荐阅读