问题描述
据我所知,Jersey 不支持深对象参数(?type[n1]=v1&type[n2]=v2
形式的参数)。
是否可以将其添加为扩展名?如果是这样,如何?
我的想法是有一个类似于 @QueryParam
的注释,比如说 @DeepObjectParam
,我会用它来注释这样的字段:
@GET
public Response(@DeepObjectParam("type") Map<String,String> type) {
// ...
}
让泽西岛注入地图。
解决方法
根据您使用的 Jersey 版本,您需要实现的接口会有所不同。在 Jersey 2.0-2.25.1 中,类是 ValueFactoryProvider
,而 2.26+ 是 ValueParamProvider
。
对于这两个类,实现将是相似的。有一种方法可以实现,它采用 Parameter
参数。我们使用此 Parameter
来检查此提供程序是否能够处理此类参数。如果检查通过,则该方法应返回提供实际参数的 Factory
或 Function
(取决于版本)。如果检查失败,则应返回 null。
比如参数用@DeepObjectParam
注解,参数类型是Map
,那么我们就应该检查这两件事。
@Override
public Function<ContainerRequest,?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
这里,DeepParamFunction
是一个 Function
,它接受单个 ContainerRequest
参数。它将解析查询参数,然后返回 Map
。
在您实现所需的类后,您需要在 Jersey 中注册它。同样,根据您使用的 Jersey 版本,注册会有所不同(但相似)。在这两种情况下,您都需要使用 AbstractBinder
ResourceConfig
register(new AbstractBinder() {
@Override
protected void configure() {
bind(DeepObjectParamProvider.class)
// 2.0-2.25.1 you will use ValueFactoryProvider.class
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
对于 Jersey 的两个版本,您将使用相同的 AbstractBinder
类,但导入会有所不同。在 2.0-2.25.1 中,您将在包名称中查找 hk2
。在 2.26 中,您将在包名称中查找 jersey
。另一个区别在于 to()
方法。在 2.0-2.25.1 中,您将使用 ValueFactoryProvider
,而 2.26+,您将使用 ValueParamProvider
。
这是 ValueParamProvider
的示例实现(适用于 Jersey 2.26+)。 ValueFactoryProvider
的实现将非常相似
public class DeepObjectParamProvider implements ValueParamProvider {
@Override
public Function<ContainerRequest,?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
private static boolean isStringStringMap(Parameter param) {
if (!param.getRawType().equals(Map.class)) {
return false;
}
ParameterizedType type = (ParameterizedType) param.getType();
Type[] genericTypes = type.getActualTypeArguments();
return genericTypes[0].equals(String.class) && genericTypes[1].equals(String.class);
}
@Override
public PriorityType getPriority() {
// Use HIGH otherwise it might not be used
return Priority.HIGH;
}
private static class DeepParamFunction implements Function<ContainerRequest,Map<String,String>> {
private final Parameter param;
private DeepParamFunction(Parameter param) {
this.param = param;
}
@Override
public Map<String,String> apply(ContainerRequest request) {
Map<String,String> map = new HashMap<>();
DeepObjectParam anno = param.getAnnotation(DeepObjectParam.class);
String paramName = anno.value();
MultivaluedMap<String,String> params = request.getUriInfo().getQueryParameters();
params.forEach((key,list) -> {
// do parsing of params
});
return map;
}
}
}
有关完整运行 (2.26+) 的示例,请查看 this post。对于 2.26 之前的版本,我重构了该示例并将其发布到 this Gist。
附注
在实现提供程序和调试时,不要对多次调用该方法感到惊讶。发生的情况是,在启动时,Jersey 将验证所有资源方法并确保所有参数都能够被处理。 Jersey 如何做到这一点是通过将每个参数传递给 all 提供者,直到到达一个不返回 null 的提供者。所以你拥有的资源方法越多,你的提供者被调用的次数就越多。有关详细说明,请参阅 this post。