Servlet/JSP 流程控制:枚举、异常还是其他?

发布于 2024-08-26 18:36:27 字数 2926 浏览 5 评论 0原文

我最近继承了一个使用裸 servlet 和 JSP(即:无框架)开发的应用程序。我的任务是清理错误处理工作流程。目前,工作流中的每个

都会提交给 servlet,并且根据表单提交的结果,servlet 执行以下两件事之一:

  1. 如果一切正常,servlet 要么转发,要么转发重定向到工作流程中的下一页。
  2. 如果出现问题,例如无效的用户名或密码,servlet 会转发到特定于问题情况的页面。例如,有AccountDisabled.jsp、AccountExpired.jsp、AuthenticationFailed.jsp、SecurityQuestionIn Correct.jsp 等页面。

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

  • Exception
    • 创建一个特定于我的需求的异常类,例如 AuthException。必要时继承此类以使其更加具体(例如:InvalidUsernameExceptionInvalidPasswordExceptionAccountDisabledException 等)。每当出现问题情况时,就抛出特定于该情况的异常。通过 web.xml 捕获所有异常,并使用 标记将它们路由到适当的页面。
  • 枚举
    • 采用错误代码方法,使用枚举来跟踪错误代码和描述。可以从成品的资源包中读取描述。

我更倾向于 enum 方法,因为身份验证失败并不是真正的“例外情况”,而且我没有看到向服务器日志添加混乱有任何好处。另外,我只是用另一种令人头痛的维护问题来代替。我不需要维护单独的 JSP,而是拥有单独的 Exception 类。

我计划在专门为此目的编写的 servlet 中实现“错误”处理。我还将消除所有单独的错误页面,而是设置一个 error 请求属性,其中包含要显示给用户的错误消息并转发回引荐来源网址。每个目标 servlet(登录、更改密码、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),我确信我遗漏了一些东西,或者我的方法不是最佳的。我是否走在正确的道路上,或者我是否需要重新考虑我的策略?

I recently inherited an application developed with bare servlets and JSPs (i.e.: no frameworks). I've been tasked with cleaning up the error-handling workflow. Currently, each <form> in the workflow submits to a servlet, and based on the result of the form submission, the servlet does one of two things:

  1. If everything is OK, the servlet either forwards or redirects to the next page in the workflow.
  2. If there's a problem, such as an invalid username or password, the servlet forwards to a page specific to the problem condition. For example, there are pages such as AccountDisabled.jsp, AccountExpired.jsp, AuthenticationFailed.jsp, SecurityQuestionIncorrect.jsp, etc.

I need to redesign this system to centralize how problem conditions are handled. So far, I've considered two possible solutions:

  • Exceptions
    • Create an exception class specific to my needs, such as AuthException. Inherit from this class to be more specific when necessary (e.g.: InvalidUsernameException, InvalidPasswordException, AccountDisabledException, etc.). Whenever there's a problem condition, throw an exception specific to the condition. Catch all exceptions via web.xml and route them to the appropriate page(s) with the <error-page> tag.
  • enums
    • Adopt an error code approach, with an enum keeping track of the error code and description. The descriptions can be read from a resource bundle in the finished product.

I'm leaning more toward the enum approach, as an authentication failure isn't really an "exceptional condition" and I don't see any benefit in adding clutter to the server logs. Plus, I'd just be replacing one maintenance headache with another. Instead of separate JSPs to maintain, I'd have separate Exception classes.

I'm planning on implementing "error" handling in a servlet that I'm writing specifically for this purpose. I'm also going to eliminate all of the separate error pages, instead setting an error request attribute with the error message to display to the user and forwarding back to the referrer. Each target servlet (Logon, ChangePassword, AnswerProfileQuestions, etc.) would add an error code to the request and redirect to my new servlet in the event of a problem. My new servlet would look something like this:

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);
}

Being fairly inexperienced with Java EE (this is my first real exposure to JSPs and servlets), I'm sure there's something I'm missing, or my approach is suboptimal. Am I on the right track, or do I need to rethink my strategy?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痴情换悲伤 2024-09-02 18:36:27

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

我自己更喜欢抛出异常。这样更加清晰简洁,并且具有更好的可重用性、可维护性和可测试性。使用抛出 ValidatorExceptionvalidate() 方法设计一个 Validator 接口。相应地实现所需的验证器。收集验证器并在 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.xml 中将其定义为 。您可以为每种类型的异常和/或HTTP状态代码定义单独的错误页面。

顺便说一下,这也是一般 MVC 框架的工作原理。

Exception/error handling in (web) applications is a sensitive subject. Some may opt for throwing hard exceptions and catching them in a single place, others may opt for passing around a collection of error messages.

I myself prefer throwing exceptions. This is more clear and concise and better reuseable, maintainable and testable. Design a Validator interface with a validate() method which throws ValidatorException. Implement the desired validators accordingly. Collect the validators and run them one by one in a try/catch block and collect the exceptions there. E.g.

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

And then usually forward the request back to the same page (where you entered the form) and display the error message somewhere next to the input fields or in top or bottom of the form. That's more user-friendly than a different error page which would require the user to click one more button/link to get the form back and the user has to remember/figure what the error actually was.

All standard error messages can of course be stored in an enum, or more preferably in an external resource file, e.g. a properties file. This way it's easier to maintain and also easier to add multiple languages to your webapp.

For unrecoverable errors, such as a dead database or a bug in the code (runtime errors, internal server errors, etc), I'd just let the exception go through all the layers so that you can "catch" it by a generic and custom error page which you can define as <error-page> in web.xml. You can define a separate error page for each type of Exception and/or HTTP status code.

This all is by the way also how the average MVC framework works.

眼藏柔 2024-09-02 18:36:27

如果您使用异常,则可以重用默认的错误处理基础结构。如果您将异常在堆栈中冒泡,或者将日志配置为不记录它们或不记录堆栈跟踪,则抛出异常不一定会弄乱您的服务器日志。

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

If you use Exceptions, you can reuse the default ErrorHandling infrastructure. Throwing exceptions doesn't have to litter your server log if you bubble them up the stack, or configure your log to either not log them, or not log stack traces.

Your error handler can then display an appropriate message/navigation/resumption based on Exception type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文