首页 > 解决方案 > 将不同的状态码绑定到处理未捕获异常的 servlet 中的 HttpServletResponse

问题描述

在我的项目中,我定义了几种类型的异常,它们应该映射到相应的响应状态代码

// map to status code 400
public class BadRequest400Exception extends RuntimeException {}

// map to status code 401
public class Unauthorized401Exception extends RuntimeException {}

// map to status code 404
public class NotFound404Exception extends RuntimeException {}

请注意,我不想在抛出它们的原始 servlet 中捕获这些异常。这就是为什么我不检查它们。换句话说,我不想拥有这样的东西

public class BusinessLogicServlet extends HttpServlet {
  
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
     try {
         // some business logic
         throw new Unauthorized401Exception();
     } catch (Exception e) {
         resp.sendError(401, "...");
     }
  }

这实际上违背了我在一个中心位置一起处理这些运行时异常的最初目的。为此,我定义了一个专门的 servlet 来处理所有抛出的未捕获异常。它将根据异常类型将请求转发到适当的错误页面

网页.xml:

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/WEB-INF/exception</location>
</error-page>

<!-- ... -->

<servlet>
    <servlet-name>ExceptionCentralProcessingServlet</servlet-name>
    <servlet-class>foo.bar.baz.ExceptionCentralProcessingServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>ExceptionCentralProcessingServlet</servlet-name>
    <url-pattern>/WEB-INF/exception</url-pattern>
</servlet-mapping>

ExceptionCentralProcessingServlet:

public class ExceptionCentralProcessingServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
     
     Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

     if (throwable instanceof BadRequest400Exception) {
        // I want to change status code from 500 to 400 in this case
        // which is not working
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        // also not working
        // response.sendError(HttpServletResponse.SC_BAD_REQUEST);
        request
              .getRequestDispatcher("/WEB-INF/views/error-pages/error-page-400.jsp")
              .forward(request, response);
        return;
     }
     
     if (throwable instanceof NotFound404Exception) {
        // I want to change status code from 500 to 404 in this case
        // which is not working
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        // also not working
        // response.sendError(HttpServletResponse.SC_NOT_FOUND);
        request
              .getRequestDispatcher("/WEB-INF/views/error-pages/error-page-404.jsp")
              .forward(request, response);
        return;
     }
  }
}

当我测试它时,这似乎是部分工作。当从 servlet 抛出 BadRequest400Exception 时,请求被转发到 error-page-400.jsp。但是,响应状态码仍然是500,尽管我已经通过response.setStatus(HttpServletResponse.SC_BAD_REQUEST);. 根据javax.servlet.http.HttpServlet 中的更改响应代码,这应该可以工作,但实际上并没有成功。我也尝试了另一种方法sendErrorresponse.sendError(400)但这也行不通。

我想知道如何将 http 状态代码(默认值似乎是 500)更改为 servlet 中为<exception-type>处理从其他 servlet 抛出的未捕获异常声明的其他值。

谢谢

标签: javajspservlets

解决方案


如果您的目标是在主 servlet 之外的集中位置捕获和处理异常,则更简单的方法可能是使用filter。过滤器能够更改响应状态代码。

一个非常基本的示例如下所示:

public class ExceptionFilter implements Filter {
    @Override
    public void init(FilterConfig config) throws ServletException {
        // nothing to do
    }

    @Override
    public void destroy() {
        // nothing to do
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            chain.doFilter(request, response);
        } catch BadRequest400Exception e) {
            // however you want to handle this exception
        } catch Unauthorized401Exception e) {
            // however you want to handle this exception
        }
        // etc...
    }
}

请注意,您还需要filter-mappingweb.xml.

在本例中,过滤器只是简单地“包装”了 servlet 请求/响应。任何从 servlet 抛出的未捕获异常都可以通过过滤器中的 try/catch 捕获,例如设置响应状态代码、转发到 JSP、写入日志等。


推荐阅读