2010-03-30 53 views
2

我最近繼承了一個用裸servlet和JSP開發的應用程序(即:沒有框架)。我一直負責清理錯誤處理工作流程。目前,在工作流中每個<form>提交到一個servlet,並基於表單提交的結果時,servlet做兩件事情之一:Servlet/JSP流量控制:枚舉,例外還是其他?

  1. 如果一切正常,該servlet向前或重定向到下一個工作流程中的頁面。
  2. 如果出現問題,例如無效的用戶名或密碼,則servlet將轉發到特定於問題條件的頁面。例如,有諸如AccountDisabled.jsp,AccountExpired.jsp,AuthenticationFailed.jsp,SecurityQuestionIncorrect.jsp等

頁我需要重新設計這個系統,以集中問題的條件是如何處理的。到目前爲止,我已經考慮了兩種可能的解決方案:

  • Exception小號
    • 創建具體到我的需求的異常類,如AuthException。繼承此類,以便在必要時更具體(例如:InvalidUsernameExceptionInvalidPasswordExceptionAccountDisabledException等)。無論什麼時候出現問題,都要拋出一個特定於條件的異常。通過web.xml捕獲所有異常,並使用<error-page>標籤將它們路由到相應的頁面。
  • enum小號
    • 採用的錯誤代碼方法,將具有錯誤代碼和描述的一個enum保持跟蹤。這些描述可以從成品中的資源包中讀取。

我更傾向朝着enum方法,作爲認證失敗是不是一個真正的「異常情況」,我沒有看到的牽絆到服務器日誌任何好處。另外,我只是用另一個替換一個維護頭痛。而不是單獨的JSP來維護,我會有單獨的Exception類。

我正計劃在一個servlet中實現「錯誤」處理,我專門爲此編寫了這個servlet。我還要消除所有單獨的錯誤頁面,而是設置一個error請求屬性並顯示錯誤消息以顯示給用戶並轉發給referrer。每個目標servlet(登錄,ChangePassword,AnswerProfileQuestions等)都會向請求添加錯誤代碼,並在出現問題時重定向到新的servlet。我的新的servlet會是這個樣子:

public enum Error { 
    INVALID_PASSWORD(5000, "You have entered an invalid password."), 
    ACCOUNT_DISABLED(5002, "Your account has been disabled."), 
    SESSION_EXPIRED(5003, "Your session has expired. Please log in again."), 
    INVALID_SECURITY_QUESTION(5004, "You have answered a security question incorrectly."); 

    private final int code; 
    private final String description; 

    Error(int code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public int getCode() { 
     return code; 
    } 

    public String getDescription() { 
     return description; 
    } 
}; 

protected void doGet(HttpServletRequest request, HttpServletResponse response) 
     throws ServletException, IOException { 
    String sendTo = "UnknownError.jsp"; 
    String message = "An unknown error has occurred."; 

    int errorCode = Integer.parseInt((String)request.getAttribute("errorCode"), 10); 

    Error errors[] = Error.values(); 
    Error error = null; 

    for (int i = 0; error == null && i < errors.length; i++) { 
     if (errors[i].getCode() == errorCode) { 
      error = errors[i]; 
     } 
    } 

    if (error != null) { 
     sendTo = request.getHeader("referer"); 
     message = error.getDescription(); 
    } 

    request.setAttribute("error", message); 

    request.getRequestDispatcher(sendTo).forward(request, response); 
} 

protected void doPost(HttpServletRequest request, HttpServletResponse response) 
     throws ServletException, IOException { 
    doGet(request, response); 
} 

是與Java EE的(這是我第一次真正接觸到JSP和servlet)相當缺乏經驗,我敢肯定有我丟失的東西,或者我的做法是次優的。我在正確的軌道上,還是我需要重新考慮我的策略?

回答

2

(網絡)應用程序中的異常/錯誤處理是一個敏感話題。有些人可能會選擇拋出強硬的異常並將它們捕捉到一個地方,其他人可能會選擇傳遞一系列錯誤消息。

我自己更喜歡拋出異常。這更清晰簡潔,更好的重複使用性,可維護性和可測試性。設計一個Validator接口與validate()方法拋出ValidatorException。相應地實施所需的驗證器。收集驗證器並在try/catch塊中逐個運行它們並收集異常。例如。

Map<String, String> messages = new HashMap<String, String>(); 
for (Validator validator : validators) { 
    try { 
     validator.validate(value); 
    } catch (ValidatorException e) { 
     messages.add(fieldname, e.getMessage()); 
    } 
} 

然後通常將請求轉發回相同頁面(即進入形式)和地方旁邊的輸入字段或頂部或形式的底部顯示錯誤消息。這比用不同的錯誤頁面更加用戶友好,這將需要用戶點擊一個按鈕/鏈接來獲取表單,並且用戶必須記住/確定錯誤實際上是什麼。

當然,所有標準錯誤消息都可以存儲在enum中,或者更優選地存儲在外部資源文件中,例如,一個屬性文件。這樣就更容易維護,並且更容易將多種語言添加到您的Web應用程序中。

對於不可恢復的錯誤,如死數據庫或代碼中的錯誤(運行時錯誤,內部服務器錯誤等),我只是讓異常遍歷所有層,以便您可以「抓住」它通過一個通用的和自定義的錯誤頁面,你可以定義爲<error-page>web.xml。您可以爲每種類型的Exception和/或HTTP狀態代碼定義單獨的錯誤頁面。

這一切都是平均MVC框架的工作方式。

1

如果使用異常,則可以重新使用默認的ErrorHandling基礎結構。拋出異常並不需要亂扔你的服務器日誌,如果你把它們放到堆棧上,或者配置你的日誌不要記錄它們,或者不記錄堆棧跟蹤。

然後,您的錯誤處理程序可以根據Exception類型顯示適當的消息/導航/恢復。

+0

當然,它會起作用,但我同意@Christopher Parker認爲這是對例外的濫用 - 許多導致重定向的場景並不是特例,而是一條常見的和預期的路徑(只有一個這是無效的)。 – 2010-03-30 18:34:10

+0

我不認爲我同意 - 即使是該帖子的語言: 「如果出現問題,例如無效的用戶名或密碼,則servlet會轉發到特定於問題條件的頁面 - 儘管我認爲這是有問題的用戶輸入無效密碼是否是例外情況 - 實際上這是錯誤處理。僅僅因爲用戶經常做某些事情並不會讓它變得非常特殊 - 只有當它由於某種錯誤而偏離標準流程時,尤其如此。一個本地代碼沒有上下文來解釋。 – jayshao 2010-03-31 22:38:00