You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 13:01:08