【问题标题】:JAX-RS Application on the root context - how can it be done?根上下文上的 JAX-RS 应用程序 - 怎么做?
【发布时间】:2012-06-03 21:47:40
【问题描述】:

我想让我的 JAX-RX 应用程序在根上下文中启动,这样我的 URL 将是

http://example.com/restfullPath

而不是

http://example.com/rest/restfullPath

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

@ApplicationPath("/rest/*")

到这里

@ApplicationPath("/*")

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

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

好像这是JBOSS论坛上的asked before,但解决方案并不实用

【问题讨论】:

    标签: java jax-rs


    【解决方案1】:

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

    不幸的是,自远古以来,Servlet 规范只允许少数几种将 servlet 映射到 URL 路径的方法。 Section 12.2 of the spec 中给出的 Servlet 3.0 的当前选项--遗憾的是仅提供 PDF 格式,因此无法按部分链接--是:

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

    该规范的同一部分还针对应用匹配规则的顺序有特定的规则,但简短的版本是这样的:要让您的资源类在上下文根处响应请求,您必须使用 @987654335 @ 或 /* 作为路径。如果您使用/,那么您将替换容器的默认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>
    

    或者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");
        }
    }
    

    由于匹配规则的编写方式,扩展模式胜过默认 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() {}
    }
    

    通过以上内容,您基本上可以将请求翻译如下:

    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
    

    3) 意识到上一个选项基本上是 URL 重写并使用现有实现,例如 Apache's mod_rewriteTuckey rewrite filterocpsoft Rewrite

    【讨论】:

      【解决方案2】:

      我找到了另一个涉及内部 Jersey 类的解决方案,我认为它可能还不是 JAX-RS 规范的一部分。 (基于:http://www.lucubratory.eu/simple-jerseyrest-and-jsp-based-web-application/

      web.xml

      <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        <display-name>jersey-rest-jsp-frame-1</display-name>
      
        <filter>
          <filter-name>jersey</filter-name>
          <filter-class>
            com.sun.jersey.spi.container.servlet.ServletContainer
          </filter-class>
          <init-param>
            <param-name>
              com.sun.jersey.config.property.JSPTemplatesBasePath
            </param-name>
            <param-value>/WEB-INF/jsp</param-value>
          </init-param>
          <init-param>
            <param-name>
              com.sun.jersey.config.property.WebPageContentRegex
            </param-name>
            <param-value>
              (/(image|js|css)/?.*)|(/.*\.jsp)|(/WEB-INF/.*\.jsp)|
              (/WEB-INF/.*\.jspf)|(/.*\.html)|(/favicon\.ico)|
              (/robots\.txt)
            </param-value>
          </init-param>
        </filter>
        <filter-mapping>
          <filter-name>jersey</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>
      </web-app>
      

      WEB-INF/jsp/index.jsp

      <%@ page contentType="text/html; charset=UTF-8" language="java" %>
      
      <html>
      <body>
      <h2>Hello ${it.foo}!</h2>
      </body>
      </html>
      

      IndexModel.java

      package example;
      
      import com.sun.jersey.api.view.Viewable;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;
      import javax.ws.rs.core.Context;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.core.Response;
      import java.net.URI;
      import java.util.HashMap;
      
      @Path("/")
      @Produces(MediaType.TEXT_HTML)
      public class IndexModel {
      
          @GET
          public Response root() {
            return Response.seeOther(URI.create("/index")).build();
          }
      
          @GET
          @Path("index")
          public Viewable index(@Context HttpServletRequest request) {
            HashMap<String, String> model = new HashMap<String, String>();
            model.put("foo","World");
            return new Viewable("/index.jsp", model);
          }
      }
      

      这似乎可行,但我想知道它是否/将成为 JAX-RS 规范/实现的一部分。

      【讨论】:

        【解决方案3】:

        您可以尝试在您的 servlet 容器中查找 DefaultServlet,并在 web.xml 中手动为其添加 servlet-mapping 以处理 *.html、*.jsp 或任何其他页面文件。

        例如对于 Tomcat 5.5,此处描述为:http://tomcat.apache.org/tomcat-5.5-doc/default-servlet.html

        【讨论】:

          【解决方案4】:

          在另一篇文章中引用 @damo for Jersey 2.0

          “或者,您可以通过某种重定向来完成某些操作。例如,使用Pre-matching Filter。我从未做过这样的事情,但文档建议“您甚至可以修改请求 URI “。”

          【讨论】:

            【解决方案5】:

            请改用@ApplicationPath("/")(不带星号)。这将对您的情况有所帮助。

            这是一个示例 REST Web 服务:

            1. JaxRsActivator.java

            package com.stackoverflow;
            
            import javax.ws.rs.ApplicationPath;
            import javax.ws.rs.core.Application;
            
            @ApplicationPath("/")
            public class JaxRsActivator extends Application {
            }
            

            2. HelloService.java

            package com.stackoverflow;
            
            import javax.ws.rs.GET;
            import javax.ws.rs.Path;
            import javax.ws.rs.Produces;
            import javax.ws.rs.core.MediaType;
            
            @Path("/hello")
            public class HelloService {
                @GET
                @Produces(MediaType.TEXT_HTML)
                public String hello() {
                    return "hello";
                }
            }
            

            我使用 Eclipse 将此动态 Web 项目导出到名为 helloservice.war 的 WAR 文件中,并将其部署到在我的本地计算机上运行的 WildFly。网址:http://localhost:8080/helloservice/hello

            访问此链接时返回:

            hello
            

            【讨论】:

            • 我不明白这是如何解决这个问题的。但如果有人好奇,这里是这个答案的代码来自的博客:buraktas.com/resteasy-example-without-using-a-web-xml
            • 我是根据自己的经验写的。您的链接没有任何您认为与我的代码相同的代码。我的代码是通用模板,而不是特定的专有代码。如果您想宣传,这不是这样做的正确地方。
            猜你喜欢
            • 2016-03-22
            • 2014-12-03
            • 1970-01-01
            • 1970-01-01
            • 2016-12-06
            • 2015-11-04
            • 2014-11-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多