簡體   English   中英

在Spring Boot中處理異常的正確方法

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM