Spring @RequestMapping 用于除 /api/ 或 /rest/ 之外的所有内容(否定正则表达式中的特定单词)

Spring @RequestMapping for everything except /api/ or /rest/ (negate specific word in regex)

我需要一个 Spring @RequestMapping,我将使用它来匹配所有非 /api/... 路径,例如 /products,以便将它们转发到我的 /(通过 return "forward:/";) 视图控制器,它实际上提供 index.html 内容并导致前端 (React) 应用程序被提供,以便前端应用程序可以处理此路径以呈现页面。

请注意,隐含的 @RequestMapping 无法匹配 /,因为它会以无限递归和 WhosebugError 结束。也可能值得排除 index.html

换句话说,我希望我的 /api/products 由 Spring 引导应用程序处理 "locally",其中 /products/ 应该被转发以呈现 React 应用程序。没有 Apache 或 Nginx 代理 - 由于静态资源映射,React 应用程序由 Spring 提供服务。

我在这方面研究了很多类似的 Whosebug 问题,其中 none 个有效。

重要说明:我在 Whosebug 上看到了问题应该得到解决的案例,只是因为最初它看起来像工作(/products/ 转发和 /api/products 由 API 控制器处理),但实际上并非如此,因为提议的正则表达式匹配 所有内容 /api/products 仅由 Spring 处理映射优先级(更具体 "wins"),但如果不存在其他控制器,则相同的路径实际上会匹配该模式。

这会导致不适当的 404(或未找到映射)错误处理 - 调用任何甚至不存在的 /api/something 端点最终都会转发到 index.html 我找到的所有答案中,这我想避免。 访问不存在的 /api/something 端点应该以没有找到处理程序 Spring 错误结束,而不是在转发中。 这种解决方案的一个例子是最受欢迎的,喜欢:@RequestMapping(value = "{_:^(?!api).*$}")

问题是,无论我尝试什么模式,它最终都会转发我所有的测试用例(比如 /api /api/ /api/x /page/ /page/2 或 none.

只是我尝试过的一些模式示例:

@RequestMapping(value = "{_:^(?!index\.html|api).*$}")
@RequestMapping(value = "{x:^(?!api).*$}") 
@RequestMapping(value = "/{path:^(?!api/).*$}")
@RequestMapping(value = "/{dataType:^.*(?!api).*$}")
@RequestMapping(value = "/{arg:(?!sitemap.xml|api|index.html).*$}")
@RequestMapping(value = "{arg:^(?!api|!index.html).*$}/**")
@RequestMapping(value = "{_:^(?!index\.html|api).*$}")

问题仍然有效,但对于需要解决方案的人,我可以建议在过滤器级别转发:

 @Bean
    public FilterRegistrationBean nonApiRequestToRootPathForwarderFilterRegistrationbean() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new Filter() {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                HttpServletRequest request1 = (HttpServletRequest) request;
                if (!request1.getRequestURI().startsWith("/api/") && !request1.getRequestURI().equals("/")) {
                    RequestDispatcher requestDispatcher = request.getRequestDispatcher("/");
                    requestDispatcher.forward(request, response);
                    return;
                }

                chain.doFilter(request, response);
            }
        });
        return filterFilterRegistrationBean;
    }

看起来原来的正则表达式确实有效(例如 "{_:^(?!index\.html|api).*$}")。

问题是,Jetty 显示 404 是在内部转发到 /error 页面,该页面也是该控制器映射的主题!有效地从 public 资源转发到前端应用,而不是呈现错误页面