Java 使用JAX-RS将两个查询参数合并到一个对象中

Java 使用JAX-RS将两个查询参数合并到一个对象中,java,jax-rs,Java,Jax Rs,我有一些资源句柄方法,其中包含几十个@QueryParam参数和@Default,大致按主题分组(分页/排序、筛选、身份验证)。这真的很麻烦,我想简化一下。好的是,这些参数是按主题(分页、排序、筛选等)分组的,因此我可以将整个参数集减少为4种方法 我怎样才能做到这一点 通常,我想从以下几点开始: @GET public Response findAll( @QueryParam("sort") @DefaultValue("name") List<String> sort,

我有一些资源句柄方法,其中包含几十个
@QueryParam
参数和
@Default
,大致按主题分组(分页/排序、筛选、身份验证)。这真的很麻烦,我想简化一下。好的是,这些参数是按主题(分页、排序、筛选等)分组的,因此我可以将整个参数集减少为4种方法

我怎样才能做到这一点

通常,我想从以下几点开始:

@GET
public Response findAll(
    @QueryParam("sort") @DefaultValue("name") List<String> sort,
    @QueryParam("from") UUID fromId
) {
  // Validate sort
  // Validate fromId
}
@GET
公众反应(
@QueryParam(“排序”)@DefaultValue(“名称”)列表排序,
@QueryParam(“from”)UUID fromId
) {
//验证排序
//验证fromId
}
为此:

@GET
public Response findAll(@Context Pagination pagination) { // Inject pagination
  // Yeah, small code! Yeah, modularity!
}

// Create the pagination somewhere else.

public Pagination createPagination(@Context UriInfo uriInfo) {
  Optional<UUID> fromId = extractFromId(uriInfo); // retrieve "from" from uriInfo
  List<String> sort = extractSort(uriInfo); // retrieve "sort" from uriInfo
  Pagination pagination = new Pagination();
  pagination.setFromId(fromId);
  pagination.setSort(sort);
  // Validate pagination
  return pagination;
}
@GET
公共响应findAll(@Context Pagination Pagination){//Inject Pagination
//是的,小代码!是的,模块化!
}
//在其他地方创建分页。
公共分页创建分页(@Context UriInfo UriInfo){
可选fromId=extractFromId(uriInfo);//从uriInfo检索“from”
列表排序=提取排序(uriInfo);//从uriInfo检索“排序”
分页分页=新分页();
分页。setFromId(fromId);
分页。设置排序(排序);
//验证分页
返回分页;
}

注意:正如我在示例中所展示的,我并不介意自己编写更多的代码,但我无法忍受方法中有太多的参数,并且阅读
@QueryParam
+
@DefaultValue

如果您使用的是JAX-RS 2.0,那么您可以使用,它允许您将任意
@XxxParam
带注释的属性和
@Context
对象注入任意bean类。比如说

public class Bean {
    @QueryParam("blah")
    String blah;
}

@GET
public Response get(@BeanParam Bean bean) {}
public static class Pagination {

    private final List<String> sort;
    private final Optional<String> from;

    public Pagination(@QueryParam("sort") List<String> sort, 
                      @QueryParam("from") Optional<String> from) {
        this.sort = sort;
        this.from = from;
    }

    public List<String> getSort() { return sort; }
    public Optional<String> getFrom() { return from; }
}
如果想要不兼容,甚至可以将其注入构造函数。比如说

public class Bean {
    @QueryParam("blah")
    String blah;
}

@GET
public Response get(@BeanParam Bean bean) {}
public static class Pagination {

    private final List<String> sort;
    private final Optional<String> from;

    public Pagination(@QueryParam("sort") List<String> sort, 
                      @QueryParam("from") Optional<String> from) {
        this.sort = sort;
        this.from = from;
    }

    public List<String> getSort() { return sort; }
    public Optional<String> getFrom() { return from; }
}
OptionalParamProvider
的好处是,它允许您在任何需要注入
@FormParam
@QueryParam
@PathParm
和所有其他
@XxxParam
的地方使用
Optional

我不知道您使用的是什么JAX-RS实现,但上述内容应该适用于所有实现。下面是一个Jersey测试用例,使用。您可以像运行任何其他JUnit测试一样运行该类

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Optional;
import javax.ws.rs.BeanParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import org.junit.Test;

public class BeanParamTest extends JerseyTest {

    @Provider
    public static class OptionalParamProvider implements ParamConverterProvider {

        @Override
        public <T> ParamConverter<T> getConverter(Class<T> rawType, 
                                                  Type genericType, 
                                                  Annotation[] annotations) {
            if (Optional.class != rawType) {
                return null;
            }

            return (ParamConverter<T>)new ParamConverter<Optional>() {

                @Override
                public Optional fromString(String value) {
                    return Optional.ofNullable(value);
                }

                @Override
                public String toString(Optional value) {
                    return value.toString();
                }  
            };
        }  
    }

    public static class Pagination {

        private final List<String> sort;
        private final Optional<String> from;

        public Pagination(@QueryParam("sort") List<String> sort, 
                          @QueryParam("from") Optional<String> from) {
            this.sort = sort;
            this.from = from;
        }

        public List<String> getSort() { return sort; }
        public Optional<String> getFrom() { return from; }
    }

    @Path("bean")
    public static class PaginationResource {

        @GET
        public String get(@BeanParam Pagination pagination) {
            StringBuilder sb = new StringBuilder();
            sb.append(pagination.getSort().toString());
            if (pagination.getFrom().isPresent()) {
                sb.append(pagination.getFrom().get());
            }
            return sb.toString();
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(PaginationResource.class)
                .register(OptionalParamProvider.class);
    }

    @Test
    public void should_return_all_sort_and_from() {
        Response response = target("bean")
                .queryParam("sort", "foo")
                .queryParam("sort", "bar")
                .queryParam("from", "baz")
                .request().get();
        assertEquals(200, response.getStatus());
        String message = response.readEntity(String.class);
        assertThat(message, containsString("foo"));
        assertThat(message, containsString("bar"));
        assertThat(message, containsString("baz"));
        System.out.println(message);
        response.close();
    }
}
import java.lang.annotation.annotation;
导入java.lang.reflect.Type;
导入java.util.List;
导入java.util.Optional;
导入javax.ws.rs.BeanParam;
导入javax.ws.rs.GET;
导入javax.ws.rs.Path;
导入javax.ws.rs.QueryParam;
导入javax.ws.rs.core.Response;
导入javax.ws.rs.ext.ParamConverter;
导入javax.ws.rs.ext.ParamConverterProvider;
导入javax.ws.rs.ext.Provider;
导入org.glassfish.jersey.server.ResourceConfig;
导入org.glassfish.jersey.test.JerseyTest;
导入静态org.hamcrest.CoreMatchers.containssString;
导入静态org.junit.Assert.assertEquals;
导入静态org.junit.Assert.assertThat;
导入org.junit.Test;
公共类BeanParamTest扩展了JerseyTest{
@提供者
公共静态类OptionalParamProvider实现ParamConverterProvider{
@凌驾
公共参数转换器getConverter(类rawType,
类型genericType,
注释[]注释){
if(可选的.class!=rawType){
返回null;
}
返回(ParamConverter)新的ParamConverter(){
@凌驾
公共可选fromString(字符串值){
返回可选的可空值(值);
}
@凌驾
公共字符串toString(可选值){
返回值.toString();
}  
};
}  
}
公共静态类分页{
私有最终列表排序;
私人最终选择从;
公共分页(@QueryParam(“sort”)列表排序,
@QueryParam(“发件人”)可选发件人){
this.sort=排序;
this.from=from;
}
公共列表getSort(){return sort;}
公共可选getFrom(){return from;}
}
@路径(“bean”)
公共静态类分页资源{
@得到
公共字符串get(@BeanParam Pagination Pagination){
StringBuilder sb=新的StringBuilder();
sb.append(pagination.getSort().toString());
if(分页.getFrom().isPresent()){
sb.append(pagination.getFrom().get());
}
使某人返回字符串();
}
}
@凌驾
公共资源配置(){
返回新的ResourceConfig(PaginationResource.class)
.注册(可选ParamProvider.class);
}
@试验
public void应该返回\u all\u sort\u和\u from(){
响应=目标(“bean”)
.queryParam(“排序”、“foo”)
.queryParam(“排序”、“条”)
.queryParam(“from”,“baz”)
.request().get();
assertEquals(200,response.getStatus());
字符串消息=response.readEntity(String.class);
资产(消息,包含字符串(“foo”));
资产(消息,包含字符串(“条”));
资产(消息,包含字符串(“baz”));
System.out.println(消息);
response.close();
}
}
这是运行测试所需的唯一Maven依赖项

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.19</version>
    <scope>test</scope>
</dependency>

org.glassfish.jersey.test-framework.providers
jersey-test-framework-provider-grizzly2
2.19
测试

如果您使用的是JAX-RS 2.0,请参阅@peeskillet,这看起来确实很有趣。我认为不可能在构造函数中注入或使用任何钩子。我非常希望有一个
可选的
成员字段,而不是一个可为空的
UUID
。您可以将
@Context-UriInfo
注入bean类。不要注释
可选
字段,在getter中,通过