如何避免API Servlet中异常处理代码重复?
消除Servlet异常处理代码重复的方案
1. 抽象基类Servlet(最易落地的方案)
创建一个抽象的BaseApiServlet继承HttpServlet,把重复的异常处理逻辑统一放在基类的doGet/doPost方法中,只留一个抽象方法让子类实现具体业务逻辑:
public abstract class BaseApiServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); } private void processRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("application/json"); try { // 调用子类的业务逻辑方法 handleBusiness(req, resp); } catch (ServiceException1 e) { writeErrorResponse(resp, 400, e.getMessage()); } catch (ServiceException2 e) { writeErrorResponse(resp, 403, e.getMessage()); } // 依次处理剩余的4-5种服务层异常 } // 子类需实现的业务逻辑入口 protected abstract void handleBusiness(HttpServletRequest req, HttpServletResponse resp) throws Exception; // 统一生成JSON错误响应的工具方法 private void writeErrorResponse(HttpServletResponse resp, int status, String message) throws IOException { resp.setStatus(status); String json = String.format("{\"code\":%d,\"message\":\"%s\"}", status, message); resp.getWriter().write(json); } }
现有所有API Servlet只需修改继承关系,把原有的业务代码移到handleBusiness方法中即可;新增Servlet时直接继承基类实现该方法,完全不用重复写异常处理代码。
2. 异常处理Filter(无侵入式方案)
Filter完全可以捕获Servlet抛出的异常,只需在doFilter方法中用try-catch包裹请求链路,统一处理异常:
@WebFilter(urlPatterns = "/api/*") // 拦截所有API路径的请求 public class ApiExceptionFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse resp = (HttpServletResponse) response; resp.setContentType("application/json"); try { chain.doFilter(request, response); } catch (ServiceException1 e) { writeErrorResponse(resp, 400, e.getMessage()); } catch (ServiceException2 e) { writeErrorResponse(resp, 403, e.getMessage()); } // 处理剩余的服务层异常 } private void writeErrorResponse(HttpServletResponse resp, int status, String message) throws IOException { resp.setStatus(status); String json = String.format("{\"code\":%d,\"message\":\"%s\"}", status, message); resp.getWriter().write(json); } }
这个方案不需要修改任何现有Servlet代码,只需添加Filter并配置拦截路径即可生效,适合不想改动现有代码结构的场景。
3. 全局异常处理Servlet(基于容器配置)
通过web.xml的<error-page>标签,将特定异常类型映射到专门的错误处理Servlet:
<web-app> <error-page> <exception-type>com.yourpackage.ServiceException1</exception-type> <location>/error-handler</location> </error-page> <error-page> <exception-type>com.yourpackage.ServiceException2</exception-type> <location>/error-handler</location> </error-page> <!-- 依次配置剩余的服务层异常 --> </web-app>
再编写一个ErrorHandlerServlet统一处理异常并生成JSON响应:
@WebServlet("/error-handler") public class ErrorHandlerServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { handleError(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { handleError(req, resp); } private void handleError(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("application/json"); Throwable exception = (Throwable) req.getAttribute("javax.servlet.error.exception"); if (exception instanceof ServiceException1) { writeErrorResponse(resp, 400, exception.getMessage()); } else if (exception instanceof ServiceException2) { writeErrorResponse(resp, 403, exception.getMessage()); } // 处理剩余的服务层异常 } private void writeErrorResponse(HttpServletResponse resp, int status, String message) throws IOException { resp.setStatus(status); String json = String.format("{\"code\":%d,\"message\":\"%s\"}", status, message); resp.getWriter().write(json); } }
该方案完全基于容器配置,无需修改现有Servlet代码,适合实现容器级别的全局异常统一处理。
内容的提问来源于stack exchange,提问作者John Little
相关产品推荐
相关产品推荐

