2014-09-05 88 views
2

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

假設我正在構建一個管理考試的系統。比方說,我有以下資源:

  • 學生(studentId,名字,姓氏,...)
  • 課程(courseId,姓名,...)
  • 考試(examId,等級。 ..)

和以下操作(其中包括):

  • GET /學生/ {} studentId
  • GET /場/ {courseId}
  • POST /學生/ {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爲Student [135]不存在。「)

的問題是,我怎麼這兩個ortogonal層次合併成一個單一異常類層次結構?

理想我想有follwing異常類:

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原則(我希望只在一個地方定義每個常量值)?

+0

我認爲你需要在這裏看一下KISS原則而不是DRY。從開發角度來看,我希望能夠快速查找是什麼導致了異常,或者當我拋出一個異常時會發生什麼。我不想拖拉一個複雜的層次來做到這一點。 – tom 2014-09-05 15:10:39

回答

1

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

例外不應按名稱傳輸原因。在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."); 
     } 

    } 

後來,當你將開發應用程序,你將可能會改變例外的設計,這種成分可以讓你做到這一點。當你使用繼承而不是組合時,你的代碼變成了繼電器對偶,這就抑制了改變和重用的潛力。

1

不要將太多的信息放入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,你可能想重用,而不是自己滾動。如果(並且只有在!)你擔心你的異常類變得太笨重時,你應該考慮把它分成多個類。

+0

但是,當我需要創建一個新的ExampleRestServiceException實例時,我必須提供所有的屬性。例如,應該拋出一個無效的studentId異常,其errorCode爲1,錯誤消息「ID爲[115]的學生不存在」。和一個httpdStatus代碼404或400,取決於studentId來自何處,將分散在整個代碼庫中(可能會從多個不同的操作拋出相同的異常)。這正是我想要避免的第一位。 – 2014-09-05 15:16:04

1

llogiq的另一種方法是有一個異常構建器或工廠。 (a)提供一致的例外,(b)如果開發人員需要檢查代碼,則將代碼保存在一個地方(c)如果不需要構建單個異常知道。