6

我创建了一个具有多个子资源的 JAX-RS 服务 (MyService),每个子资源都是 MySubResource 的子类。选择的子资源类是根据 MyService 路径中给定的参数选择的,例如:

@Path("/") @Provides({"text/html", "text/xml"}) 
public class MyResource {
  @Path("people/{id}") public MySubResource getPeople(@PathParam("id") String id) {
    return new MyPeopleSubResource(id);
  }
  @Path("places/{id}") public MySubResource getPlaces(@PathParam("id") String id) {
    return new MyPlacesSubResource(id);
  }
}

其中 MyPlacesSubResource 和 MyPeopleSubResource 都是 MySubResource 的子类。

MySubResource 定义为:

public abstract class MySubResource {
  protected abstract Results getResults();

  @GET public Results get() { return getResults(); }

  @GET @Path("xml") 
  public Response getXml() {
    return Response.ok(getResults(), MediaType.TEXT_XML_TYPE).build();  
  }

  @GET @Path("html") 
  public Response getHtml() {
    return Response.ok(getResults(), MediaType.TEXT_HTML_TYPE).build();  
  }
}

结果由相应的 MessageBodyWriters 处理,具体取决于响应的 mimetype。

虽然这可行,但它会导致像 /people/Bob/html 或 /people/Bob/xml 这样的路径,我真正想要的是 /people/Bob.html 或 /people/Bob.xml

有人知道如何完成我想做的事情吗?

4

4 回答 4

7

老话题,但这是我最近使用 Jersey 解决的问题;也许它会帮助别人。

Jersey 支持通过使用请求过滤器将接受的内容类型指定为 URI 中的文件扩展名。Jersey 提供了一个UriConnegFilter(URI 内容协商过滤器)对象,您可以扩展该对象以将特定扩展转换为内容类型。然后将该过滤器作为初始参数包含在 Jersey 应用程序中。

由于这一切听起来都很模糊,这是我项目中的一个具体示例:

我希望能够将 URL 末尾的“.json”和“.xml”解释为客户端分别需要 JSON 格式或 XML 格式的内容。为此,我UriConnegFilter这样扩展:

package my.filter.package;

import com.sun.jersey.api.container.filter.UriConnegFilter;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.core.MediaType;


public class MediaTypeFilter extends UriConnegFilter {
  private static final Map<String, MediaType> mappedMediaTypes = new HashMap<String, MediaType>(2);

  static {
    mappedMediaTypes.put("json", MediaType.APPLICATION_JSON_TYPE);
    mappedMediaTypes.put("xml", MediaType.APPLICATION_XML_TYPE);
  }

  public MediaTypeFilter() {
    super(mappedMediaTypes);
  }
}

然后,因为我使用 Jersey 作为 Servlet,所以我MediaTypeFilter在 web.xml 中添加了:

<servlet>
  <servlet-name>My Jersey Servlet</servlet-name>
  <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>my.resource.package</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.LoggingFilter;
                 my.filter.package.MediaTypeFilter;
                 com.sun.jersey.api.container.filter.PostReplaceFilter;
                 com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter;
                 com.sun.jersey.api.container.filter.LoggingFilter</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

有了这个,Jersey 将 URI 上的扩展名转换为指定的媒体类型,并删除扩展名。这适用于根资源和子资源,因为它在整个 URI 上运行。对于您的特定示例, /people/Bob.xml 将变为 /people/Bob 并且Accept请求中的标头将更改为“application/xml”(覆盖任何现有Accept标头)。

h,

-彼得

于 2012-01-06T23:18:52.740 回答
1

您可能会编写一些 servlet 路由来解决它。但实际上,您应该使用内容类型来执行此操作。

@GET @Path("/") @Produces(MediaType.APPLICATION_XML)
public Response getXml() { ... }

@GET @Path("/") @Produces(MediaType.APPLICATION_HTML)
public Response getHtml() { ... }

然后,JAX-RS 提供者将根据客户的请求确定要调用的内容。更好的是,您可以让 JAXB 和 RestEASY 为您完成这一切!

@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/{id}")
public MyObject getXml(@PathParam("typeDocument") String id) {
 myObjectService.get(id);
}


@XmlRootElement(name="myObject")
public class MyObject {
// Some properties
}

有关 Spring 的一个很好的示例,请参见http://java.dzone.com/articles/resteasy-spring

于 2010-06-03T11:11:23.040 回答
1

这是一个老话题,我相信你已经明白了,但是对于那些访问这个页面的人来说,Resteasy 有一个简单的方法来实现你所需要的。这称为基于 URL 的内容协商。

在此处查看详细信息:http: //docs.jboss.org/resteasy/docs/2.0.0.GA/userguide/html_single/index.html#media_mappings

基本上,您需要在 web.xml 文件中添加一个上下文参数,该参数将告诉 Resteasy 将 URL 的后缀映射到特定的内容类型:

<context-param>
    <param-name>resteasy.media.type.mappings</param-name>
    <param-value>html : text/html, json : application/json, xml : application/xml</param-value>
</context-param>

这样,访问 /people/Bob.xml 与访问 /people/Bob 并指定 Accept-Encoding: application/xml 相同。

于 2011-03-24T08:54:16.770 回答
1

解决此问题的一种方法是您可以在 @javax.ws.rs.Path 中使用正则表达式捕获。

@Path("people/{id:[^/]+?}{format:(\\.[^/]*?)?}")
@GET
public MySubResource getPeople(@PathParam("id") String id, @PathParam("format") String format) {
    // remove the "." from the start of "format" if it is not null
    return new MySubResource(id, format);
}

然后在您的子资源中:

public abstract class MySubResource {
    final protected String format;

    protected MySubResource(String id, String format) {
        this.format = format;
    }

    protected abstract Results getResults();

    @GET
    public Response get() {
       return Response.ok(getResults(), this.format).build();  
    }
}

小心使用正则表达式。我举了一个例子,但你可能想要收紧表达以确保没有任何东西漏掉。

解决此问题的另一种方法是更改​​捕获 {id} 的位置并在那里使用正则表达式。不要@Path("id") MySubResource public getPeople(@PathParam("id") String id)捕获 id,而是从 getPeople() 中删除 id 捕获并更改 MySubResource,如下所示:

 @Path("people")
 public MySubResource getPeople() {
    return new MyPeopleSubResource();
 }

public abstract class MySubResource {
  protected abstract Results getResults();

  @GET
  @Path("{id}")
  public Results get() { return getResults(); }

  @GET
  @Path("{id}.xml") 
  public Response getXml() {
    return Response.ok(getResults(), MediaType.TEXT_XML_TYPE).build();  
  }

  @GET
  @Path("{id}.html") 
  public Response getHtml() {
    return Response.ok(getResults(), MediaType.TEXT_HTML_TYPE).build();  
  }
}

根据您的数据结构的组织方式以及何时需要知道“id”参数,这两种情况都需要权衡取舍。我不是特别喜欢正则表达式,因为它们真的很难正确,但在这种情况下它是可能的。

于 2010-07-20T01:22:25.657 回答