40

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

http://example.com/restfullPath

并不是

http://example.com/rest/restfullPath

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

@ApplicationPath("/rest/*")

对此

@ApplicationPath("/*")

但似乎它接管了诸如 /index.html 之类的服务文件

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

好像之前在JBOSS论坛上问过这个问题,但解决方案不是很实用

4

5 回答 5

45

它可能不是一个错误,而是 Servlet 规范的限制。如何处理 JAX-RS 的细节@ApplicationPath是特定于实现的,我不能代表所有实现,但我猜典型的方法是简单地将其用作 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>

或使用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 的 mod_rewriteTuckey 重写过滤器ocpsoft Rewrite

于 2013-05-25T06:57:58.250 回答
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>

索引模型.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 规范/实现的一部分。

于 2013-06-12T23:13:19.293 回答
1

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

例如,对于 Tomcat 5.5,它在此处进行了描述:http: //tomcat.apache.org/tomcat-5.5-doc/default-servlet.html

于 2012-06-04T11:18:21.510 回答
0

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

“或者,您可以通过某种重定向来完成某些事情。例如,使用预匹配过滤器。我从未做过这样的事情,但文档建议“您甚至可以修改请求 URI”。 "

于 2013-06-26T22:02:00.670 回答
0

改为使用@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 项目导出到名为 as 的 WAR 文件中,helloservice.war并将其部署到在我的本地计算机上运行的 WildFly。它的网址:http://localhost:8080/helloservice/hello.

访问此链接时,它返回:

hello
于 2015-08-06T17:19:18.717 回答