Как вы сопоставляете несколько параметров запроса с полями bean по запросу Джерси GET?
Класс сервиса имеет операцию @GET
, которая принимает несколько параметров. Эти параметры передаются в качестве параметров запроса в сервисный вызов @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();
}
Как вы это сделаете? Возможно ли это?
Ответы
Ответ 1
Вы можете использовать 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();
}
Поставщик будет автоматически вызван.
Ответ 2
В Джерси 2.0 вы захотите использовать BeanParam, чтобы обеспечить то, что вы ищете в обычном стиле Джерси.
Из приведенной выше страницы doc вы можете использовать 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;
}
Я предпочитаю публичные свойства по моему параметру beans, но вы также можете использовать геттеры/сеттеры и частные поля.
Ответ 3
Попробуйте что-нибудь подобное. Используйте UriInfo, чтобы получить все параметры запроса на карте и попытаться получить к ним доступ. Это делается для передачи отдельных параметров.
// showing only the relavent code
public FindResponse find( @Context UriInfo allUri ) {
MultivaluedMap<String, String> mpAllQueParams = allUri.getQueryParameters();
String prop1 = mpAllQueParams.getFirst("prop1");
}
Ответ 4
Вы можете создать настраиваемый поставщик.
@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;
}
Ответ 5
Возможно, вы захотите использовать следующий подход. Это очень стандартное решение, и там нет хаков. Вышеупомянутое решение также работает, но несколько взломанно, потому что оно предполагает, что оно касается только тела запроса, тогда как оно извлекает данные из контекста.
В моем случае я хотел создать аннотацию, которая позволила бы сопоставлять параметры запроса "limit" и "offset" с одним объектом. Решение выглядит следующим образом:
@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;
}
и bean
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));
}