Java 如何在GET请求中将多个查询参数映射到bean的字段?
服务类具有接受多个参数的Java 如何在GET请求中将多个查询参数映射到bean的字段?,java,jersey,jax-rs,Java,Jersey,Jax Rs,服务类具有接受多个参数的@GET操作。这些参数作为查询参数传递给@GET服务调用 @GET @Path("find") @Produces(MediaType.APPLICATION_XML) public FindResponse find(@QueryParam("prop1") String prop1, @QueryParam("prop2") String prop2, @Query
@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();
}
// showing only the relavent code
public FindResponse find( @Context UriInfo allUri ) {
MultivaluedMap<String, String> mpAllQueParams = allUri.getQueryParameters();
String prop1 = mpAllQueParams.getFirst("prop1");
}
你会怎么做?这可能吗 试试这样的东西。使用UriInfo将所有请求参数获取到映射中并尝试访问它们。这是在传递单个参数的地方完成的
@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();
}
// showing only the relavent code
public FindResponse find( @Context UriInfo allUri ) {
MultivaluedMap<String, String> mpAllQueParams = allUri.getQueryParameters();
String prop1 = mpAllQueParams.getFirst("prop1");
}
//仅显示相关代码
公共查找响应查找(@Context UriInfo allUri){
多值Map mpAllQueParams=allUri.getQueryParameters();
字符串prop1=mpAllQueParams.getFirst(“prop1”);
}
您可以创建自定义提供程序
@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;
}
@Provider
@组成部分
公共类RequestParameterBeanProvider实现MessageBodyReader
{
//保存uri
@上下文
私人UriInfo UriInfo;
//需要从中封送的bean类的列表
//请求参数
私有列表paramBeanClassList;
//参数bean的枚举字段列表
私有映射enumFieldMap=新HashMap();
@凌驾
公共布尔值可读取(类类型、类型genericType、注释[]注释、MediaType MediaType)
{
返回paramBeanClassList.contains(类型);
}
@凌驾
公共对象readFrom(类类型、类型genericType、注释[]注释、MediaType MediaType、多值Map HttpHeader、InputStream entityStream)引发IOException、WebApplicationException
{
多值Map params=uriInfo.getQueryParameters();
对象newRequestParamBean;
尝试
{
//创建参数bean
newRequestParamBean=type.newInstance();
//填充参数bean属性
for(条目参数:params.entrySet())
{
String key=param.getKey();
对象值=param.getValue().iterator().next();
//设置属性
setProperty(newRequestParamBean、键、值);
}
}
捕获(例外e)
{
抛出新的WebApplicationException(e,500);
}
返回newRequestParamBean;
}
public void setParamBeanClassList(列表paramBeanClassList)
{
this.paramBeanClassList=paramBeanClassList;
}
您可以使用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;
}
};
}
}
将自动调用提供程序。在Jersey 2.0中,您需要使用它无缝地提供您想要的普通球衣样式 从上面链接的文档页面,您可以使用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上的公共属性,但如果您愿意,也可以使用getter/setter和private字段。您可能希望使用以下方法。这是一个非常标准的兼容解决方案,没有任何漏洞。上面的解决方案也可以使用,但有些漏洞,因为它表明它只处理请求而它从上下文中提取数据 在我的例子中,我想创建一个注释,允许将查询参数“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 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));
}
@GET
@路径(获取一个)
public SingleResult getOne(@NotNull@PathParam(ID_PARAM)String itemId,@SelectorParam选择器){
Item=auditService.getOneItem(ItemId.create(ItemId));
返回singleResult(mapOne(Item.class,ItemDTO.class)。选择(选择器)。使用(Item));
}
我知道我的答案不适用于特定的上下文。但由于WEB传输机制应该与核心应用程序分离,因此可以选择更改为其他WEB框架。就像Spring webmvc一样,所有这些都是开箱即用的。我正试图避免这种情况。如果我可以避免,它会更干净id取决于我的服务类上的UriInfo类。使用接受的答案肯定会更干净。这显然更干净。您可以添加如何使用此提供程序吗?感谢如果您无法使用jaxrs注释来注释bean,此解决方案效果最佳。我相信,从Jersey 2.0开始,您将希望使用@Patrick,请添加您的注释作为答案。如果有新的信息,那么可以添加一个答案,这样用户就可以在不查看评论的情况下找到新信息。@JonathanSpooner现在2.0已经发布了,这似乎比我第一次发表评论时的想法要好,所以我采纳了你的建议。谢谢!这种实现方法当您稍后尝试在其他资源方法中使用@QueryParam
时,g POJO绑定可能会产生问题。最好创建自己的注释并使用它,而不是@QueryParam
。相反,创建名为QueryBeanParam:@Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})的注释(RetentionPolicy.RUNTIME)public@interface QueryBeanParam{}
。然后,实现InjectableProvider
而不是ParameterBeanProvider中的InjectableProvider
。最后,在您的资源中使用@QueryBeanParam
:公共FindResponse find(@querybeanparameterbeanparamberbean){…}
对于结果来说,这似乎是一项繁重的工作。它在REST方法或所讨论的DTO上都不可见,因此需要不知道它的开发人员进行一些挖掘。@BeanParam答案感觉它更接近于JAX-RS的注释驱动风格。有什么方法可以避免放置@QueryParam吗
对于我的bean中的每个属性,如果我希望它们都可用
@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));
}