2012-11-20 47 views
11

我想使用Joda的DateTime来查看Jersey中的查询参数,但Jersey不支持此功能。我假设执行InjectableProvider是添加DateTime支持的正确方法。使用Joda DateTime作为泽西参数?

有人可以指点我的的吗?还是有其他方法值得推荐? (我知道我可以在我的代码中从DateString转换,但这似乎是一个较小的解决方案)。

谢谢。

解决方案:

我修改下面吉利的回答使用JAX-RS,而不是吉斯的@Context注射机构。

更新:如果未在服务方法参数中注入UriInfo,则可能无法正常工作。

import com.sun.jersey.core.spi.component.ComponentContext; 
import com.sun.jersey.spi.inject.Injectable; 
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; 
import java.util.List; 
import javax.ws.rs.QueryParam; 
import javax.ws.rs.WebApplicationException; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.Status; 
import javax.ws.rs.core.UriInfo; 
import javax.ws.rs.ext.Provider; 
import org.joda.time.DateTime; 

/** 
* Enables DateTime to be used as a QueryParam. 
* <p/> 
* @author Gili Tzabari 
*/ 
@Provider 
public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime> 
{ 
    private final UriInfo uriInfo; 

    /** 
    * Creates a new DateTimeInjector. 
    * <p/> 
    * @param uriInfo an instance of {@link UriInfo} 
    */ 
    public DateTimeInjector(@Context UriInfo uriInfo) 
    { 
     super(DateTime.class); 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a) 
    { 
     return new Injectable<DateTime>() 
     { 
      @Override 
      public DateTime getValue() 
      { 
       final List<String> values = uriInfo.getQueryParameters().get(a.value()); 

       if(values == null || values.isEmpty()) 
        return null; 
       if (values.size() > 1) 
       { 
        throw new WebApplicationException(Response.status(Status.BAD_REQUEST). 
         entity(a.value() + " may only contain a single value").build()); 
       } 
       return new DateTime(values.get(0)); 
      } 
     }; 
    } 
} 

回答

5

这是一个取决于Guice的实现。您可以使用不同的喷油器进行小的修改:

import com.google.inject.Inject; 
import com.sun.jersey.core.spi.component.ComponentContext; 
import com.sun.jersey.spi.inject.Injectable; 
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; 
import java.util.List; 
import javax.ws.rs.QueryParam; 
import javax.ws.rs.WebApplicationException; 
import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.Status; 
import javax.ws.rs.core.UriInfo; 
import javax.ws.rs.ext.Provider; 
import org.joda.time.DateTime; 

/** 
* Enables DateTime to be used as a QueryParam. 
* <p/> 
* @author Gili Tzabari 
*/ 
@Provider 
public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime> 
{ 
    private final com.google.inject.Provider<UriInfo> uriInfo; 

    /** 
    * Creates a new DateTimeInjector. 
    * <p/> 
    * @param uriInfo an instance of {@link UriInfo} 
    */ 
    @Inject 
    public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo) 
    { 
     super(DateTime.class); 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a) 
    { 
     return new Injectable<DateTime>() 
     { 
      @Override 
      public DateTime getValue() 
      { 
       final List<String> values = uriInfo.get().getQueryParameters().get(a.value()); 
       if (values.size() > 1) 
       { 
        throw new WebApplicationException(Response.status(Status.BAD_REQUEST). 
         entity(a.value() + " may only contain a single value").build()); 
       } 
       if (values.isEmpty()) 
        return null; 
       return new DateTime(values.get(0)); 
      } 
     }; 
    } 
} 

没有Guice绑定。 @Provider是一个JAX-RS注释。 Guice只需要能够注入UriInfo并且Jersey-Guice提供了该绑定。

+0

谢谢,吉利。我没有使用Guice,但我不知道UriInfo是否可以注入JAX-RS的@Context注入? – HolySamosa

+0

是的,@Context适用于注入UriInfo。我还稍微修改了您的代码以处理空日期时间查询参数的情况。我的改编作为对我的问题的编辑发布。谢谢!! – HolySamosa

1

@吉利,对不起,我没有必要的信誉直接评论你的帖子,但请您:

  • 添加用于您的实现import语句?
  • 添加一个如何将所有与Guice绑定的例子?

非常感谢您提前。

M.


问题

我有兴趣做同样的HolySamosa,我用Guice的为好,但我面对的下方的问题。

如果我添加:

bind(DateTimeInjector.class); 
在我 GuiceServletContextListener

,我得到:

java.lang.RuntimeException: 
The scope of the component class com.foo.mapping.DateTimeInjector must be a singleton 

,如果我在DateTimeInjector类添加@Singleton,我得到:

GRAVE: The following errors and warnings have been detected with resource and/or provider classes: 
SEVERE: Missing dependency for method public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime) at parameter at index 1 
SEVERE: Method, public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime), annotated with GET of resource, class com.foo.ThingService, is not recognized as valid resource method. 

装运通知/ SOLUTIONS

  • 注意你用什么注解(不像我)!例如,我实际上使用@PathParam而不是@QueryParam
  • 在您的服务中,您不需要在方法的签名中有UriInfo uriInfo。只是功能参数应该足够,它应该工作,无论是否存在UriInfo
  • Guice需要配置下面才能拿起注射器。

实施例:

// Configure Jersey with Guice: 
Map<String, String> settings = new HashMap<String, String>(); 
settings.put(PackagesResourceConfig.PROPERTY_PACKAGES, "com.foo.mapping"); 
serve("/*").with(GuiceContainer.class, settings); 

完整的解决方案

import java.util.List; 

import javax.ws.rs.PathParam; 
import javax.ws.rs.WebApplicationException; 
import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.Status; 
import javax.ws.rs.core.UriInfo; 
import javax.ws.rs.ext.Provider; 

import org.joda.time.DateTime; 

import com.google.inject.Inject; 
import com.foo.utils.DateTimeAdapter; 
import com.sun.jersey.core.spi.component.ComponentContext; 
import com.sun.jersey.spi.inject.Injectable; 
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; 

/** 
* Enables DateTime to be used as a PathParam. 
*/ 
@Provider 
public class DateTimeInjector extends PerRequestTypeInjectableProvider<PathParam, DateTime> { 
    private final com.google.inject.Provider<UriInfo> uriInfo; 

    /** 
    * Creates a new DateTimeInjector. 
    * 
    * @param uriInfo 
    *   an instance of {@link UriInfo} 
    */ 
    @Inject 
    public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo) { 
     super(DateTime.class); 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public Injectable<DateTime> getInjectable(final ComponentContext context, final PathParam annotation) { 
     return new Injectable<DateTime>() { 
      @Override 
      public DateTime getValue() { 
       final List<String> values = uriInfo.get().getPathParameters().get(annotation.value()); 

       if (values == null) { 
        throwInternalServerError(annotation); 
       } 

       if (values.size() > 1) { 
        throwBadRequestTooManyValues(annotation); 
       } 

       if (values.isEmpty()) { 
        throwBadRequestMissingValue(annotation); 
       } 

       return parseDate(annotation, values); 
      } 

      private void throwInternalServerError(final PathParam annotation) { 
       String errorMessage = String.format("Failed to extract parameter [%s] using [%s]. This is likely to be an implementation error.", 
         annotation.value(), annotation.annotationType().getName()); 
       throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build()); 
      } 

      private void throwBadRequestTooManyValues(final PathParam annotation) { 
       String errorMessage = String.format("Parameter [%s] must only contain one single value.", annotation.value()); 
       throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); 
      } 

      private void throwBadRequestMissingValue(final PathParam annotation) { 
       String errorMessage = String.format("Parameter [%s] must be provided.", annotation.value()); 
       throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); 
      } 

      private DateTime parseDate(final PathParam annotation, final List<String> values) { 
       try { 
        return DateTimeAdapter.parse(values.get(0)); 
       } catch (Exception e) { 
        String errorMessage = String.format("Parameter [%s] is formatted incorrectly: %s", annotation.value(), e.getMessage()); 
        throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); 
       } 
      } 

     }; 
    } 
} 
+0

我已经更新了我的答案。 – Gili

+0

@Marc:在编辑问题中看到非Guice版本。 – HolySamosa

+0

@HolySamosa:如果不在服务的方法参数中提供'UriInfo uriInfo',那么使用没有Guice的'@ Context'似乎不起作用。 你是否只有DateTime参数或其他泽西特定参数?例如'public void saveResource(@Context UriInfo uriInfo,@QueryParam(“date”)DateTime date){...}' 或者您是否使用其他一些“技巧”? –

2

另一种选择,处理约达日期时间的发送的客户端 - 服务器之间对象是编组/使用适配器和相应的注释将它们明确解组。 原则是将其编组为Long对象,而反编组使用Long对象为构造函数调用实例化新的DateTime对象。 Long对象通过getMillis方法获取。 要使这项工作,指定在具有DateTime对象的类使用该适配器:

@XmlElement(name="capture_date") 
@XmlJavaTypeAdapter(XmlDateTimeAdapter.class) 
public DateTime getCaptureDate() { return this.capture_date; } 
public void setCaptureDate(DateTime capture_date) { this.capture_date = capture_date; } 

然后写适配器和XML类来封装的长对象:

import javax.xml.bind.annotation.adapters.XmlAdapter; 
import org.joda.time.DateTime; 
import org.joda.time.DateTimeZone; 

/** 
* Convert between joda datetime and XML-serialisable millis represented as long 
*/ 
public class XmlDateTimeAdapter extends XmlAdapter<XmlDateTime, DateTime> { 

    @Override 
    public XmlDateTime marshal(DateTime v) throws Exception { 

     if(v != null) 
      return new XmlDateTime(v.getMillis()); 
     else 
      return new XmlDateTime(0); 


    } 

    @Override 
    public DateTime unmarshal(XmlDateTime v) throws Exception { 

     return new DateTime(v.millis, DateTimeZone.UTC); 
    } 
} 


import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 

/** 
* XML-serialisable wrapper for joda datetime values. 
*/ 
@XmlRootElement(name="joda_datetime") 
public class XmlDateTime { 

    @XmlElement(name="millis") public long millis; 

    public XmlDateTime() {}; 

    public XmlDateTime(long millis) { this.millis = millis; } 

} 

如果所有去计划,DateTime对象应该通过使用适配器进行编组/解组;通过在适配器中设置断点来检查这一点。