JAX-RS应用程序在根上下文中 - 如何完成?

Era*_*dan 35 java jax-rs

我想让我的JAX-RX应用程序从根上下文开始,所以我的URL将是

http://example.com/restfullPath

并不是

http://example.com/rest/restfullPath

我从这里切换了我的应用程序注释

@ApplicationPath("/rest/*")
Run Code Online (Sandbox Code Playgroud)

对此

@ApplicationPath("/*")
Run Code Online (Sandbox Code Playgroud)

但它似乎接管了服务文件,如/index.html

有没有办法在根应用程序上下文中运行JAX-RS但仍然提供静态页面?

看来这是之前在JBOSS论坛上提出的,但解决方案并不实用

Rya*_*art 40

它可能不是Servlet规范限制的错误.有关如何@ApplicationPath处理JAX-RS的细节是特定于实现的,我不能代表所有实现,但我猜想典型的方法是简单地将它用作servlet URL模式.以Jersey的ServletContainerInitializer实现为例,你会发现该addServletWithApplication()方法负责创建servlet和映射来处理请求,你可以看到它确实使用了来自@ApplicationPathJersey ServletContainer映射的路径.路径.

不幸的是,从远古时代开始,Servlet规范只允许少数几种将servlet映射到URL路径的方法.Servlet 3.0的当前选项,在规范的第12.2节中给出 - 仅以PDF格式提供,因此不能按部分链接 - 是:

  • /.../*其中,初始/...值为零个或多个路径元素
  • *.<ext><ext>匹配的扩展名在哪里
  • 空字符串,仅映射到空路径/上下文根
  • /,单斜杠,表示上下文中的"默认"servlet,它处理与其他任何内容不匹配的任何内容
  • 任何其他字符串,被视为要匹配的文字值

规范的相同部分对匹配规则应该应用的顺序也有特定的规则,但是短版本是这样的:要在上下文根处进行资源类应答请求,您必须使用//*作为路径.如果您使用/,那么您将替换容器的默认servlet,它通常负责处理静态资源.如果你使用/*,那么你就太贪婪了,并说它应该始终匹配所有的东西,并且永远不会调用默认的servlet.

因此,如果我们接受我们在由servlet URL模式的限制确定的框内,我们的选项相当有限.以下是我能想到的:

1)使用@ApplicationPath("/"),并通过名称或扩展名将静态资源显式映射到容器的默认servlet(在Tomcat和Jetty中命名为"default",不确定其他人).在web.xml中,它看起来像

<!-- All html files at any path -->
<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- Specifically index.html at the root -->
<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>/index.html</url-pattern>
</servlet-mapping>
Run Code Online (Sandbox Code Playgroud)

或者像ServletContextInitializer一样

public class MyInitializer implements ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx) {
        ctx.getServletRegistration("default").addMapping("*.html");
        ctx.getServletRegistration("default").addMapping("/index.html");
    }
}
Run Code Online (Sandbox Code Playgroud)

由于编写匹配规则的方式,扩展模式胜过默认servlet,因此只需要在每个静态文件扩展名之间添加映射,只要这些扩展模块与可能出现的任何"扩展"之间没有重叠.你的API.这非常接近您链接的论坛帖子中提到的不受欢迎的选项,我只是提到它的完整性并添加了ServletContextInitializer部分.

2)将您的API映射到/rest/*,并使用过滤器来识别API请求并将其转发到该路径.这样,您就可以打破servlet URL模式框,并以任何方式匹配URL.例如,假设您的所有REST调用都是以"/ foo"开头或完全是"/ bar"的路径,而所有其他请求都应该转到静态资源,那么类似于:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.regex.Pattern;

@WebFilter(urlPatterns = "/*")
public class PathingFilter implements Filter {
    Pattern[] restPatterns = new Pattern[] {
            Pattern.compile("/foo.*"),
            Pattern.compile("/bar"),
    };

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            String path = ((HttpServletRequest) request).getServletPath();
            for (Pattern pattern : restPatterns) {
                if (pattern.matcher(path).matches()) {
                    String newPath = "/rest/" + path;
                    request.getRequestDispatcher(newPath)
                        .forward(request, response);
                    return;
                }
            }
        }
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}
Run Code Online (Sandbox Code Playgroud)

有了上述内容,您实际上将请求转换如下:

http://example.org/foo          -> http://example.org/rest/foo
http://example.org/foox         -> http://example.org/rest/foox
http://example.org/foo/anything -> http://example.org/rest/foo/anything
http://example.org/bar          -> http://example.org/rest/bar
http://example.org/bart         -> http://example.org/bart
http://example.org/index.html   -> http://example.org/index.html
Run Code Online (Sandbox Code Playgroud)

3)意识到前一个选项基本上是URL重写并使用现有的实现,例如Apache的mod_rewrite,Tuckey重写过滤器ocpsoft Rewrite.