[英]Correct way to handle exceptions in Spring Boot
我正在閱讀Spring文檔,發現從ResponseEntityExceptionHandler
創建子類是處理異常的好方法。 但是,我嘗試以不同的方式處理異常,因為我需要從TechnicalExceptions
區分BusinessExceptions
。
創建了一個名為BusinessFault
的bean,它封裝了異常詳細信息:
BusinessFault.java
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(value = Include.NON_NULL)
public class BusinessFault {
@JsonProperty(value = "category")
private final String CATEGORY = "Business Failure";
protected String type;
protected String code;
protected String reason;
protected String description;
protected String instruction;
public BusinessFault(String type, String code, String reason) {
this.type = type;
this.code = code;
this.reason = reason;
}
public BusinessFault(String type, String code, String reason, String description, String instruction) {
this.type = type;
this.code = code;
this.reason = reason;
this.description = description;
this.instruction = instruction;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getInstruction() {
return instruction;
}
public void setInstruction(String instruction) {
this.instruction = instruction;
}
public String getCATEGORY() {
return CATEGORY;
}
}
創建了一個BusinessException
類,它通過構造函數傳遞的詳細信息創建BusinessFault
bean來完成工作:
BusinessException.java
import com.rest.restwebservices.exception.fault.BusinessFault;
public abstract class BusinessException extends RuntimeException {
private BusinessFault businessFault;
public BusinessException(String type, String code, String reason) {
this.businessFault = new BusinessFault(type, code, reason);
}
public BusinessException(String type, String code, String reason, String description, String instruction) {
this.businessFault = new BusinessFault(type, code, reason, description, instruction);
}
public BusinessException(BusinessFault businessFault) {
this.businessFault = businessFault;
}
public BusinessFault getBusinessFault() {
return businessFault;
}
public void setBusinessFault(BusinessFault businessFault) {
this.businessFault = businessFault;
}
}
創建了一個特定的UserNotFoundException
類,該類從BusinessException
類擴展而來:
UserNotFoundException.java
import com.rest.restwebservices.exception.fault.BusinessFault;
import com.rest.restwebservices.exception.map.ExceptionMap;
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(BusinessFault businessFault) {
super(businessFault);
}
public UserNotFoundException(String reason) {
super(ExceptionMap.USERNOTFOUND.getType(), ExceptionMap.USERNOTFOUND.getCode(), reason);
}
public UserNotFoundException(String reason, String description, String instruction) {
super(ExceptionMap.USERNOTFOUND.getType(), ExceptionMap.USERNOTFOUND.getCode(), reason, description,
instruction);
}
}
創建了一個BusinessExceptionHandler
,但它不是一個ResponseEntityExceptionHandler
的子類,它只有一個@ControllerAdvice
注釋和一個處理所有拋出的BusinessExceptions
:
BusinessExceptionHandler.java
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.rest.restwebservices.controller.UserController;
import com.rest.restwebservices.exception.BusinessException;
import com.rest.restwebservices.exception.fault.BusinessFault;
@ControllerAdvice(basePackageClasses = UserController.class)
public class BusinessExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, BusinessException ex) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.OK);
}
}
服務層可以拋出UserNotFoundException
:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findById(Long id) {
User user = userRepository.findOne(id);
if (user == null)
throw new UserNotFoundException("The ID " + id + " doesn't behave to any user!");
return user;
}
}
它工作正常。 但我想知道這是否是處理異常的不良做法?
我的異常處理有點問題。 原則上,捕獲runtime exceptions
,處理它們並將它們發送到客戶端是絕對可以的,客戶端可能是使用您的REST服務並將錯誤響應作為JSON對象獲取的人。 如果你設法告訴他他做錯了什么以及他能做些什么,太棒了! 當然,它會增加一些復雜性,但使用該API可能很容易和舒適。
但想想后端開發人員也可以使用你的代碼。 特別是UserService中的public User findById(Long id)
方法是模糊的。 這樣做的原因是您進行了BusinessException
,特別是未選中 UserNotFoundException
。
如果我加入了你的(后端)團隊,並且我要使用該服務編寫一些業務邏輯,我將非常確定我對該方法的期望:我傳遞一個用戶ID並返回一個User
對象,如果它是如果沒有找到或為null
。 這就是我寫這樣的代碼的原因
User user = userService.findById("42A");
if (user == null) {
// create a User or return an error or null or whatever
} else {
// proceed
}
但是,我永遠不會知道,第一個條件永遠不會成立,因為你永遠不會返回null
。 我怎么知道我必須抓住一個例外?
編譯器告訴我抓住它嗎? 不,因為沒有檢查。
我會查看你的源代碼嗎? 一定不行! 你的情況非常簡單。 UserNotFoundException
可能在另一個類中的一百行代碼中的另一個方法中引發。 無論如何,有時我無法查看它,因為UserService
只是依賴項中的編譯類。
我讀過JavaDoc嗎? 哈哈哈。 讓我們說,無論如何,50%的時間我不會,而另外50%你忘記記錄它。
因此,開發人員必須等到他的代碼被使用(通過客戶端或單元測試)才能看到它不能按預期工作,迫使他重新設計到目前為止他編碼的內容。 如果您的整個API都采用這種方式設計,那么未經檢查的異常突然出現,它可能會非常煩人,實際上它需要花費時間和金錢並且很容易避免。
我使用類似的方法來處理異常。 但在我的情況下,根據錯誤狀態管理不同的處理程序(例如,存在用戶,由於某些不滿意的條件而無法注冊用戶等)。
您還可以為某些特殊情況添加通用BusinessException。 希望它能讓你感覺更好。
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.rest.restwebservices.controller.UserController;
import com.rest.restwebservices.exception.ResourceNotFoundException;
import com.rest.restwebservices.exception.PreconditionFailedException;
import com.rest.restwebservices.exception.ResourceAlreadyExistsException;
import com.rest.restwebservices.exception.fault.BusinessFault;
@ControllerAdvice(basePackageClasses = UserController.class)
public class BusinessExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, ResourceNotFoundException ex) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(PreconditionFailedException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, PreconditionFailedExceptionex) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.PRECONDITION_FAILED);
}
@ExceptionHandler(ResourceAlreadyExistsException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, ResourceAlreadyExistsException) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.CONFLICT);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.