java - 处理服务层结果的正确模式
问题描述
所以一段时间以来我一直在使用 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 中特别昂贵,我怀疑什么从长远来看,我所做的是好的。
是否有另一种正确的方式来优雅地处理业务结果层?
解决方案
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);
}
}
推荐阅读
- css - 如何使用引导程序在列之间放置行
- php - Laravel5.8 belongsTo 关系不适用于 foreach 循环
- javascript - 遇到TypeError:尝试使用react creatable select使用搜索栏时值不可迭代
- python - 对一组列的条件操作
- javascript - 如何使用 MiniCssExtractPlugin 控制 CSS 的顺序?
- python - 如何在张量流中完全执行二维插值?
- javascript - 使用 exec 节点运行 Python 脚本,然后使用 Node red 中的函数节点解析为 javascript
- vue.js - 用于调试的 Jsfiddle 不能与 Vuetify 一起运行
- android - Saripaar 验证库抛出 No rules found 错误
- c# - 在 Owin.FileSystems PhysicalFileSystem 中使用网络路径时如何修复 Windows Server IIS-10 中的 System.IO.DirectoryNotFoundException