Как создать сервис JAX-RS, где у подресурса @Path нет косой черты - PullRequest
5 голосов
/ 15 апреля 2010

Я создал службу 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 ]

7 голосов
/ 07 января 2012

Старая тема, но это то, что я недавно решил с помощью Джерси; возможно, это поможет кому-то еще.

Джерси поддерживает указание принятого типа контента в качестве расширения файла в URI с помощью фильтров запросов. Джерси предоставляет объект UriConnegFilter (фильтр согласования содержимого URI), который вы расширяете для преобразования определенных расширений в типы содержимого. Затем вы включаете этот фильтр в качестве начального параметра в приложение Джерси.

Поскольку все это звучит так расплывчато, вот конкретный пример из моего проекта:

Я хотел иметь возможность интерпретировать «.json» и «.xml» в конце URL-адреса как означающее, что клиент хотел контент в формате 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);
  }
}

Затем, поскольку я использую Джерси в качестве сервлета, я добавил 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>

После этого Джерси преобразует расширение по URI в указанный тип носителя и удаляет расширение. Это работает для корневых ресурсов и подресурсов, поскольку работает на весь URI. Для вашего конкретного примера /people/Bob.xml станет / people / Bob, а заголовок Accept в запросе будет изменен на "application / xml" (переопределяя любой существующий заголовок Accept).

НТН,

-Питер

1 голос
/ 20 июля 2010

Одним из способов решения этой проблемы является то, что вы, вероятно, можете использовать захват регулярного выражения в своем @ 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) захвата идентификатора, удалите захват идентификатора из getPeople () и измените 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». Я не особенно люблю регулярные выражения, потому что их действительно сложно понять, но в этом случае это возможно.

1 голос
/ 24 марта 2011

Это старая тема, и я уверен, что вы поняли это, но для тех, кто попадает на эту страницу, у 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: application / xml.

1 голос
/ 03 июня 2010

Вы могли бы написать некоторую маршрутизацию сервлетов, чтобы решить это. Реально, однако, вы должны использовать типы контента для этого.

@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
}

См. http://java.dzone.com/articles/resteasy-spring для хорошего примера с Spring.

...