代码之家  ›  专栏  ›  技术社区  ›  Christopher Parker

servlet/jsp流控制:枚举、异常或其他什么?

  •  2
  • Christopher Parker  · 技术社区  · 16 年前

    我最近继承了一个使用裸servlet和JSP开发的应用程序(即:没有框架)。我的任务是清理错误处理工作流。目前,每个 <form> 在向servlet提交的工作流中,servlet根据表单提交的结果执行以下两项操作之一:

    1. 如果一切正常,servlet将转发或重定向到工作流中的下一页。
    2. 如果出现问题,例如用户名或密码无效,servlet将转发到特定于问题条件的页面。例如,有accountdisabled.jsp、accountexpired.jsp、authenticationfailed.jsp、securityquestionincorrect.jsp等页面。

    我需要重新设计这个系统来集中处理问题条件。到目前为止,我已经考虑了两种可能的解决方案:

    • Exception S
      • 根据我的需要创建一个异常类,例如 AuthException . 从该类继承,以便在必要时更具体(例如: InvalidUsernameException ,请 InvalidPasswordException , AccountDisabledException 等)。每当出现问题条件时,抛出一个特定于该条件的异常。通过web.xml捕获所有异常,并使用 <error-page> 标签。
    • enum S
      • 采用错误代码方法,并使用 枚举 跟踪错误代码和描述。这些描述可以从成品的资源包中读取。

    我更倾向于 枚举 方法,因为身份验证失败并不是一个真正的“异常情况”,而且我看不到在服务器日志中增加混乱的好处。另外,我会用另一个来代替一个维护头痛。不是单独的JSP来维护,而是单独的 例外 类。

    我计划在一个servlet中实现“错误”处理,这是我专门为此编写的。我还将消除所有单独的错误页,而不是设置 error 带错误消息的请求属性将显示给用户并转发回引用网站。每个目标servlet(logon、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);
    }
    

    对JavaEE(这是我第一次真正接触JSP和servlet)相当缺乏经验,我确信我缺少了一些东西,或者我的方法是次优的。我是在正确的轨道上,还是需要重新考虑我的战略?

    2 回复  |  直到 16 年前
        1
  •  2
  •   BalusC    16 年前

    (Web)应用程序中的异常/错误处理是一个敏感主题。有些可能选择抛出硬异常并在单个位置捕获它们,另一些可能选择传递一组错误消息。

    我自己更喜欢抛出例外。这是更清晰、简洁和更好的可重用性、可维护性和可测试性。设计一 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 ,或者更优选在外部资源文件(例如属性文件)中。这样就更容易维护,也更容易向您的webapp添加多种语言。

    对于不可恢复的错误,例如死掉的数据库或代码中的错误(运行时错误、内部服务器错误等),我只让异常通过所有层,这样您就可以通过一个通用的和自定义的错误页“捕获”它,您可以将其定义为 <error-page> 在里面 web.xml . 您可以为每种类型的 Exception 和/或HTTP状态代码。

    这也是平均MVC框架的工作方式。

        2
  •  1
  •   jayshao    16 年前

    如果使用异常,则可以重用默认的错误处理基础结构。抛出异常不必在堆栈中冒泡时丢弃服务器日志,也不必将日志配置为不记录或不记录堆栈跟踪。

    然后,错误处理程序可以根据异常类型显示适当的消息/导航/恢复。