簡體   English   中英

REST服務的異常層次結構

[英]Exception hierarchy for a REST service

我正在為我的REST服務設計一個異常類層次結構,並面臨以下問題。 請注意,該解決方案必須與Java 7兼容。

假設我正在建立一個管理考試的系統。 假設我有以下資源:

  • 學生(studentId,名字,姓氏...)
  • 課程(courseId,名稱,...)
  • 考試(考試編號,年級...)

以及以下操作(以及其他操作):

  • GET /學生/ {studentId}
  • GET / courses / {courseId}
  • POST / students / {studentId} /考試(帶有包含成績和courseId的正文)

請注意,對於某些操作,courseId是作為URL的一部分提供的,而對於其他操作,則是作為請求正文的一部分提供的。

我想設計一個異常類層次結構,以一致的方式向客戶端報告錯誤。 我希望每個異常類具有以下屬性:

  • statusCode-響應的HTTP狀態代碼,
  • errorCode-錯誤的唯一標識符,
  • errorMessage-錯誤的人類可讀描述。

現在讓我們關注類似於“無效ID”的錯誤。 我有以下要求:

  • 如果將無效ID作為URL的一部分提供,則statusCode應該為404(未找到)。 如果它是作為請求主體的一部分提供的,則應為400(錯誤請求)。

  • errorCode / errorMessage應該從業務角度識別錯誤。 errorCode應該是一個可以唯一標識錯誤原因的數字(例如,1表示invalid-studentId,2表示invalid-courseId,等等),並且errorMessage應該以可人工修改的格式描述原因(例如,“ ID為ID的學生” [135]不存在。”)

問題是,如何將這兩個正交層次結構合並為一個異常類層次結構?

理想情況下,我將擁有以下異常類:

public abstract class ApiException extends Exception {

    public abstract int getStatusCode();
    public abstract int getErrorCode();
    public abstract String getErrorMessage();
}

public abstract class NotFoundException extends ApiException {

    public int getStatusCode() {
        return 404;
    }
}

public abstract class BadRequestException extends ApiException {

    public int getStatusCode() {
        return 400;
    }
}

public abstract class InvalidCourseIdException extends ApiException {

    private final String courseId;

    public InvalidCourseIdException(final String courseId) {
        this.courseId = courseId;
    }

    public int getErrorCode() {
        return 2;
    }

    public String getErrorMessage() {
        return "Course with ID [" + courseId + "] does not exist.";
    }
}

public class CourseNotFoundException extends NotFoundException, InvalidCourseException {
    public CourseNotFoundException(String courseId) {
        super(courseId);
    }
}

public class BadCourseException extends BadRequestException, InvalidCourseException {

    public BadCourseException(String courseId) {
        super(courseId);
    }
}

...

當然,Java中不提供多重繼承。 如何設計Java 7兼容的類層次結構,遵循DRY原則(我想將每個常量值僅保留在一個位置)?

不要在Exception類中放入太多信息。 帶有您提到的屬性的ExamRestServiceException完全可以。 如果要禁止某些屬性組合,則將構造函數設為私有並使用工廠方法,例如

 ...
 private ExamRestServiceException(int httpStatusCode, int errorCode, int objectId, String message) {
    // initialize your exception here
 }

 public ExamRestServiceException of(int httpStatusCode, int errorCode, int objectId, String message) {
    // check the arguments here
    return new ExamRestServiceException(httpStatusCode, errorCode, objectId, message);
 }
 ....

編輯:如果您想比僅通過文檔更好地指導您的API用戶,您當然可以提供專門的工廠方法,例如

 //e.g. needs no message, the HTTP status code is enough
 public ExamRestServiceException connectionError(int httpStatusCode) {…}

要么

 public ExamRestServiceException(int errorCode, String message) { … }

甚至針對每種錯誤情況提供一種方法。 順便說一下,有一個可以完美使用的HttpRetryException,您可能想重用而不是自己滾動。 如果(且僅當!)您擔心異常類變得太笨拙,則應考慮將其拆分為多個類。

llogiq的另一種方法是擁有異常生成器或工廠。

(a)提供一致的異常,(b)如果開發人員需要檢查代碼,則將代碼保存在一個地方 (c)使開發人員不必知道如果不需要知道如何構造單個異常。

已檢查異常的關鍵方面是您可以捕獲它們並從狀態中恢復。

異常不應按名稱傳遞原因。 如果是400或404,則無法恢復,因此應用程序應記錄狀態並中斷。

ApiException類足以啟動。

如果您害怕產生太多的代碼克隆。 您可以創建一個util類來收集所有案例。

final class ApiThrowables {

       static final int BAD_REQUEST = 400; 
       static final int NOT_FOUND   = 404;

       public static ApiException newCourseIdNotFound(String courseId) {
          return new ApiException(2, NOT_FOUND,"Course with ID [" + courseId + "] does not exist.");
       }

       public static ApiException newBadCourseId(String courseId) {
          return new ApiException(2, BAD_REQUEST,"Course ID [" + courseId + "] is not valid.");
       }

    }

稍后,當您開發應用程序時,您可能會更改例外的設計,這種組合將使您能夠做到這一點。 當您使用繼承而不是組合時,您的代碼變成了中繼對象,這抑制了更改和重用的可能性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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