How do you map multiple query parameters to the fields of a bean on Jersey GET request?

后端 未结 5 1640
傲寒
傲寒 2020-11-30 19:27

A service class has a @GET operation that accepts multiple parameters. These parameters are passed in as query parameters to the @GET service call.

5条回答
  •  庸人自扰
    2020-11-30 20:06

    You might want to use the following approach. This is a very standard-compliant solution and there are no hacks in there. The above solution also works but is somewhat hacky because it suggests it deals only with request body whereas it extracts the data from the context instead.

    In my case I wanted to create an annotation which would allow to map query parameters "limit" and "offset" to a single object. The solution is as follows:

    @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 {
    
            public InjectionResolver() {
                super(SelectorParamValueFactoryProvider.class);
            }
    
        }
    
        private static final class SelectorParamValueFactory extends AbstractContainerRequestValueFactory {
    
            @Context
            private ResourceContext  context;
    
            private Parameter parameter;
    
            public SelectorParamValueFactory(Parameter parameter) {
                this.parameter = parameter;
            }
    
            public Selector provide() {
                UriInfo uriInfo = context.getResource(UriInfo.class);
                MultivaluedMap 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);
        }
    
    }
    

    What you also need to do is registering it.

    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>() {
                        }).in(Singleton.class);
            }
    
        }
    
    }
    

    You also need the annotation itself

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

    and a 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;
        }
    
    }
    

    Then you might use it like this

    @GET
    @Path(GET_ONE)
    public SingleResult 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));
    }
    

提交回复
热议问题