【问题标题】:Hosting Static Content and JAX-RS Services Under the Same Root Context在同一根上下文下托管静态内容和 JAX-RS 服务
【发布时间】:2015-07-18 16:04:56
【问题描述】:

我们有多个打包为 WAR 的 Java Web 应用程序,所有这些都打包在一个 EAR 中。我们的 RESTful 服务是使用 JAX-RS 和版本特定的 WAR 构建的。

我们希望为每个特定版本的 WAR 添加静态内容,但对静态内容和 RESTful 服务 API 调用都使用(WAR)的根上下文,以便以下所有 URL 都可以使用:

{hostname}/v1/swagger.yaml  <-- Static Content describing the v1 API
{hostname}/v1/orders/{uid}  <-- JAX-RS RESTful API (v1)

{hostname}/v2/swagger.yaml  <-- Static Content describing the v2 API
{hostname}/v2/orders/{uid}  <-- JAX-RS RESTful API (v2)

以下部署结构是我们目前拥有的,适用于 JAX-RS 服务,但不适用于静态内容。换句话说,这些 URL 有效:

{hostname}/v1/orders/{uid}  <-- JAX-RS RESTful API (v1)
{hostname}/v2/orders/{uid}  <-- JAX-RS RESTful API (v2)

但问题是这些 URL 无法访问:

{hostname}/v1/swagger.yaml  <-- Static Content describing the v1 API
{hostname}/v2/swagger.yaml  <-- Static Content describing the v2 API

问题:我们如何让这些静态 URL 发挥作用?

这是分解后的 EAR:

example.ear

    META-INF
        application.xml

    v1.war
        swagger.yaml
        WEB-INF
            web.xml
            classes
                ApplicationV1.class
                OrdersResourceV1.class

    v2.war
        swagger.yaml
        WEB-INF
            web.xml
            classes
                ApplicationV2.class
                OrdersResourceV2.class

这是 EAR application.xml

<?xml version="1.0"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="6">
  <application-name>...</application-name>
  <module>
    <web>
      <web-uri>v1.war</web-uri>
      <context-root>/v1</context-root>
    </web>
  </module>
  <module>
    <web>
      <web-uri>v2.war</web-uri>
      <context-root>/v2</context-root>
    </web>
  </module>
  <module>
    <web>
      <web-uri>another.war</web-uri>
      <context-root>/</context-root>
    </web>
  </module>
</application>

这里是 v1 和 v2 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">
</web-app>

这是ApplicationV1.java (JAX-RS) 的实现,注意 URL 映射:

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;


@ApplicationPath("/")
public class ApplicationV1 extends Application {

  private Set<Object> singletons = new HashSet<Object>();

  public ApplicationV1() {
    singletons.add(new OrdersResourceV1());
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }

}

这是OrdersResourceV1.java (JAX-RS) 实现,注意 URL 映射:

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.*;

@RequestScoped
@Path("/orders")
public class OrdersResourceV1 {

  @GET
  @Path("/{uid}")
  @Produces("application/json;charset=UTF-8")
  public Response getOrder(@PathParam("uid") String orderUid) {
    <ommited for brevity>
  }

}

我知道如果我们将 ApplicationV*.java 的映射更改为 @ApplicationPath("/services"),静态内容将是可评估的,但 URL 将如下所示,但我们的目标是不包含任何无关的路径元素,例如这个。

{hostname}/v1/swagger.yaml  <-- Static Content describing the v1 API
{hostname}/v1/services/orders/{uid} <-- JAX-RS RESTful API (v1)

{hostname}/v2/swagger.yaml  <-- Static Content describing the v2 API
{hostname}/v2/services/orders/{uid} <-- JAX-RS RESTful API (v2)

所以我的问题是,我们如何在 WAR(/v1/v2)的根上下文下获取 JAX-RS 和静态内容?是否有不同的方法来映射 URL?或者,如果 JAX-RS 没有 URL 处理程序,是否有允许静态回退的配置选项?

我希望这可以通过 JavaEE 通用方式解决,但如果不能,我们将在 Java8 上使用 Wildfly 8.1。

【问题讨论】:

  • 问题是servlet容器有一个默认的servlet来处理静态内容。当您定义具有相同映射的 servlet 时,即/*,这将隐藏静态内容 servlet。当调用 Resteasy servlet 时,如果找不到请求的静态内容,它不会回退到默认的 servlet。我知道 Jersey 有一个配置属性要设置,它自己处理静态内容,但我不知道 Resteasy。我checked, but you can check again
  • 您可以使用一个过滤器,该过滤器可以了解何时请求针对静态内容并自行响应;您必须重现静态资源 servlet 的部分(或全部?)功能。如果您只想提供 swagger.yaml,那么您可以创建一个 JAX-RS 服务,通过从类路径加载例如文件并设置正确的标头来响应此 URL。

标签: java jakarta-ee jax-rs resteasy wildfly-8


【解决方案1】:

我找到了另一个解决方案(感谢https://stackoverflow.com/a/3582215/589525)。这适用于 JBoss 6 上的 RestEasy。只需将默认 servlet 添加到映射中

<!--existing mapping: -->
<servlet-mapping>
  <servlet-name>Resteasy</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>*</url-pattern>
</servlet-mapping>

【讨论】:

    【解决方案2】:

    所以我找到了一个可行的解决方案(经过测试)。如Resteasy Documentation - RESTEasy as a servlet Filter中所述:

    将 Resteasy 作为 Servlet 运行的缺点是您不能在与 JAX-RS 服务相同的路径中拥有像 .html 和 .jpeg 文件这样的静态资源。 Resteasy 允许您改为作为过滤器运行。如果在请求的 URL 下没有找到 JAX-RS 资源,Resteasy 将委托回基本 servlet 容器来解析 URL。

    web.xml 示例

    <web-app>
        <filter>
            <filter-name>Resteasy</filter-name>
            <filter-class>
                org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
            </filter-class>
            <init-param>
                <param-name>javax.ws.rs.Application</param-name>
                <param-value>com.restfully.shop.services.ShoppingApplication</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>Resteasy</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    

    【讨论】:

    • 即使 Resteasy 自己的文档说 servlet 过滤器已不推荐使用,我认为当我们需要同时提供静态资源和服务时,这是唯一的选择。或者我们可以做一些配置来在你的应用程序中仍然使用纯 JAX-RS?
    • @fasfsfgs 是的,文档会提到过滤器使用的弃用和它是问题的解决方案,这对我来说似乎有点奇怪,更不用说在同一段落中了。如果有什么安慰的话,即使是使用 Jersey,要启用此功能,我们也需要注册为过滤器。看起来更像是对整个 servlet 的限制,而不是 JAX-RS。至于任何其他解决方案,这是我能找到的唯一一个,除非您愿意将 servlet 映射更改为 /* 之外的其他东西,这对于 OP 来说不是一个可能的解决方案
    • 与其他已弃用的功能(resteasy.scan 属性)不同,resteasy 仍然允许您使用此功能..因此对于 servlet 3.0 容器而言,它并没有真正完全弃用..
    猜你喜欢
    • 2017-03-14
    • 1970-01-01
    • 2016-02-06
    • 2012-01-28
    • 1970-01-01
    • 2015-07-08
    • 2016-03-22
    • 1970-01-01
    相关资源
    最近更新 更多