如何将多个查询参数映射到Jersey GET请求上的bean的字段?
一个服务类有一个接受多个参数的@GET
操作。 这些参数作为查询parameter passing给@GET
服务调用。
@GET @Path("find") @Produces(MediaType.APPLICATION_XML) public FindResponse find(@QueryParam("prop1") String prop1, @QueryParam("prop2") String prop2, @QueryParam("prop3") String prop3, @QueryParam("prop4") String prop4, ...)
这些参数的列表越来越多,所以我想把它们放到一个包含所有这些参数的bean中。
@GET @Path("find") @Produces(MediaType.APPLICATION_XML) public FindResponse find(ParameterBean paramBean) { String prop1 = paramBean.getProp1(); String prop2 = paramBean.getProp2(); String prop3 = paramBean.getProp3(); String prop4 = paramBean.getProp4(); }
你会怎么做? 这甚至有可能吗?
你可以使用com.sun.jersey.spi.inject.InjectableProvider
。
import java.util.List; import java.util.Map.Entry; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.Provider; import org.springframework.beans.BeanUtils; import com.sun.jersey.api.core.HttpContext; import com.sun.jersey.api.model.Parameter; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.core.spi.component.ComponentScope; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.InjectableProvider; @Provider public final class ParameterBeanProvider implements InjectableProvider<QueryParam, Parameter> { @Context private final HttpContext hc; public ParameterBeanProvider(@Context HttpContext hc) { this.hc = hc; } @Override public ComponentScope getScope() { return ComponentScope.PerRequest; } @Override public Injectable<ParameterBean> getInjectable(ComponentContext ic, final QueryParam a, final Parameter c) { if (ParameterBean.class != c.getParameterClass()) { return null; } return new Injectable<ParameterBean>() { public ParameterBean getValue() { ParameterBean parameterBean = new ParameterBean(); MultivaluedMap<String, String> params = hc.getUriInfo().getQueryParameters(); // Populate the parameter bean properties for (Entry<String, List<String>> param : params.entrySet()) { String key = param.getKey(); Object value = param.getValue().iterator().next(); // set the property BeanUtils.setProperty(parameterBean, key, value); } return parameterBean; } }; } }
在您的资源中,您只需使用@QueryParam("valueWeDontCare")
。
@GET @Path("find") @Produces(MediaType.APPLICATION_XML) public FindResponse find(@QueryParam("paramBean") ParameterBean paramBean) { String prop1 = paramBean.getProp1(); String prop2 = paramBean.getProp2(); String prop3 = paramBean.getProp3(); String prop4 = paramBean.getProp4(); }
提供者将被自动调用。
在Jersey 2.0中 ,您将需要使用BeanParam以正常泽西风格无缝地提供您正在寻找的内容。
从上面的链接文档页面,你可以使用BeanParam做类似的事情:
@GET @Path("find") @Produces(MediaType.APPLICATION_XML) public FindResponse find(@BeanParam ParameterBean paramBean) { String prop1 = paramBean.prop1; String prop2 = paramBean.prop2; String prop3 = paramBean.prop3; String prop4 = paramBean.prop4; }
然后ParameterBean.java
将包含:
public class ParameterBean { @QueryParam("prop1") public String prop1; @QueryParam("prop2") public String prop2; @QueryParam("prop3") public String prop3; @QueryParam("prop4") public String prop4; }
我更喜欢我的参数bean的公共属性,但是如果你喜欢的话,你可以使用getters / setter和private字段。
尝试这样的事情。 使用UriInfo获取所有的请求参数到一个地图,并尝试访问它们。 这是在传递个别参数的地方完成的。
// showing only the relavent code public FindResponse find( @Context UriInfo allUri ) { MultivaluedMap<String, String> mpAllQueParams = allUri.getQueryParameters(); String prop1 = mpAllQueParams.getFirst("prop1"); }
您可以创build一个自定义提供者。
@Provider @Component public class RequestParameterBeanProvider implements MessageBodyReader { // save the uri @Context private UriInfo uriInfo; // the list of bean classes that need to be marshalled from // request parameters private List<Class> paramBeanClassList; // list of enum fields of the parameter beans private Map<String, Class> enumFieldMap = new HashMap<String, Class>(); @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return paramBeanClassList.contains(type); } @Override public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { MultivaluedMap<String, String> params = uriInfo.getQueryParameters(); Object newRequestParamBean; try { // Create the parameter bean newRequestParamBean = type.newInstance(); // Populate the parameter bean properties for (Entry<String, List<String>> param : params.entrySet()) { String key = param.getKey(); Object value = param.getValue().iterator().next(); // set the property BeanUtils.setProperty(newRequestParamBean, key, value); } } catch (Exception e) { throw new WebApplicationException(e, 500); } return newRequestParamBean; } public void setParamBeanClassList(List<Class> paramBeanClassList) { this.paramBeanClassList = paramBeanClassList; }
您可能想要使用以下方法。 这是一个非常符合标准的解决scheme,并没有在那里的黑客。 上面的解决scheme也可以工作,但是有点不好意思,因为它表明它只处理请求体而不是从上下文中提取数据。
在我的情况下,我想创build一个注释,这将允许映射查询参数“限制”和“抵消”到一个单一的对象。 解决scheme如下:
@Provider public class SelectorParamValueFactoryProvider extends AbstractValueFactoryProvider { public static final String OFFSET_PARAM = "offset"; public static final String LIMIT_PARAM = "limit"; @Singleton public static final class InjectionResolver extends ParamInjectionResolver<SelectorParam> { public InjectionResolver() { super(SelectorParamValueFactoryProvider.class); } } private static final class SelectorParamValueFactory extends AbstractContainerRequestValueFactory<Selector> { @Context private ResourceContext context; private Parameter parameter; public SelectorParamValueFactory(Parameter parameter) { this.parameter = parameter; } public Selector provide() { UriInfo uriInfo = context.getResource(UriInfo.class); MultivaluedMap<String, String> params = uriInfo.getQueryParameters(); SelectorParam selectorParam = parameter.getAnnotation(SelectorParam.class); long offset = selectorParam.defaultOffset(); if(params.containsKey(OFFSET_PARAM)) { String offsetString = params.getFirst(OFFSET_PARAM); offset = Long.parseLong(offsetString); } int limit = selectorParam.defaultLimit(); if(params.containsKey(LIMIT_PARAM)) { String limitString = params.getFirst(LIMIT_PARAM); limit = Integer.parseInt(limitString); } return new BookmarkSelector(offset, limit); } } @Inject public SelectorParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator injector) { super(mpep, injector, Parameter.Source.UNKNOWN); } @Override public AbstractContainerRequestValueFactory<?> createValueFactory(Parameter parameter) { Class<?> classType = parameter.getRawType(); if (classType == null || (!classType.equals(Selector.class))) { return null; } return new SelectorParamValueFactory(parameter); } }
你还需要做的是注册它。
public class JerseyApplication extends ResourceConfig { public JerseyApplication() { register(JacksonFeature.class); register(new InjectionBinder()); } private static final class InjectionBinder extends AbstractBinder { @Override protected void configure() { bind(SelectorParamValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class); bind(SelectorParamValueFactoryProvider.InjectionResolver.class).to( new TypeLiteral<InjectionResolver<SelectorParam>>() { }).in(Singleton.class); } } }
你也需要注解本身
@Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) public @interface SelectorParam { long defaultOffset() default 0; int defaultLimit() default 25; }
和一个豆
public class BookmarkSelector implements Bookmark, Selector { private long offset; private int limit; public BookmarkSelector(long offset, int limit) { this.offset = offset; this.limit = limit; } @Override public long getOffset() { return 0; } @Override public int getLimit() { return 0; } @Override public boolean matches(Object object) { return false; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BookmarkSelector that = (BookmarkSelector) o; if (limit != that.limit) return false; if (offset != that.offset) return false; return true; } @Override public int hashCode() { int result = (int) (offset ^ (offset >>> 32)); result = 31 * result + limit; return result; } }
那么你可以像这样使用它
@GET @Path(GET_ONE) public SingleResult<ItemDTO> getOne(@NotNull @PathParam(ID_PARAM) String itemId, @SelectorParam Selector selector) { Item item = auditService.getOneItem(ItemId.create(itemId)); return singleResult(mapOne(Item.class, ItemDTO.class).select(selector).using(item)); }