Jersey 3.1.3:404时如何继续执行下一个Servlet过滤器?
解决Jersey 3.1.3中ExceptionMapper拦截404导致无法转发到后续过滤器的问题
核心问题在于:添加自定义ExceptionMapper后,Jersey会用它捕获NotFoundException并生成响应,跳过原本ServletProperties.FILTER_FORWARD_ON_404=true配置的转发逻辑,导致请求中止,后续Servlet过滤器无法执行。
以下是两种可行的解决方案:
方案一:自定义NotFoundException的ExceptionMapper,手动触发请求转发
实现一个ExceptionMapper<NotFoundException>,在捕获异常时直接调用Servlet容器的转发机制,让请求进入后续过滤器链,而不是返回响应。
import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.Provider; @Provider public class NotFoundForwardMapper implements ExceptionMapper<NotFoundException> { @Context private HttpServletRequest request; @Context private HttpServletResponse response; @Override public jakarta.ws.rs.core.Response toResponse(NotFoundException exception) { try { // 将请求转发回Servlet容器,触发后续过滤器处理 RequestDispatcher dispatcher = request.getRequestDispatcher(request.getRequestURI()); dispatcher.forward(request, response); // 返回null告知Jersey无需再生成响应,避免覆盖转发逻辑 return null; } catch (Exception e) { // 转发失败时返回标准404响应 return jakarta.ws.rs.core.Response.status(404).build(); } } }
注意:必须保留ServletProperties.FILTER_FORWARD_ON_404=true的配置,同时这个自定义Mapper会接管所有NotFoundException的处理,若需针对特定路径(比如仅/x)转发,可在方法内添加路径判断:
String requestPath = request.getRequestURI(); if ("/x".equals(requestPath)) { // 仅对/x执行转发 RequestDispatcher dispatcher = request.getRequestDispatcher(requestPath); dispatcher.forward(request, response); return null; } else { // 其他路径按正常逻辑返回404 return jakarta.ws.rs.core.Response.status(404).entity("Resource not found").build(); }
方案二:利用Jersey的ContainerRequestFilter提前拦截未匹配的请求
如果不想修改ExceptionMapper的逻辑,可以实现ContainerRequestFilter,在请求到达资源匹配阶段前,判断当前路径是否存在对应的Jersey资源。若不存在,直接转发请求到后续过滤器。
不过这种方式需要借助Jersey内部API获取资源信息,实现相对复杂,示例如下:
import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.UriInfo; import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.internal.routing.RoutingContext; @Provider public class ForwardUnmatchedRequestFilter implements ContainerRequestFilter { @Context private HttpServletRequest request; @Context private HttpServletResponse response; @Context private ResourceModel resourceModel; @Context private RoutingContext routingContext; @Override public void filter(ContainerRequestContext context) { String requestPath = context.getUriInfo().getPath(); // 检查当前路径是否能匹配到Jersey资源 boolean hasMatchingResource = resourceModel.getResources().stream() .anyMatch(resource -> resource.getPathPattern().match(requestPath).isPresent()); if (!hasMatchingResource) { try { RequestDispatcher dispatcher = request.getRequestDispatcher(requestPath); dispatcher.forward(request, response); // 中止Jersey的请求处理流程 context.abortWith(null); } catch (Exception e) { context.abortWith(jakarta.ws.rs.core.Response.status(404).build()); } } } }
这种方式的优势是无需修改ExceptionMapper的逻辑,但依赖Jersey内部的ResourceModel和RoutingContext,升级版本时可能需要调整。
内容的提问来源于stack exchange,提问作者user27772
相关产品推荐
相关产品推荐

