让Jersey使用可选参数 [英] Get Jersey to work with Optional parameters
问题描述
我正在尝试让Jersey使用可选参数.我有一个非常简单的Web服务:
I'm trying to get Jersey to work with Optional parameters. I have a very simple web service:
@Path("helloworld")
public static class HelloWorldResource {
public static final String CLICHED_MESSAGE = "Hello World!";
@GET
@Produces("text/plain")
public String getHello(@QueryParam("maybe") Optional<String> maybe) {
return CLICHED_MESSAGE;
}
}
和一个简单的工具:
public static void main(String[] arg) throws IOException {
ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
String baseUri = "http://localhost:8080/api/";
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(URI.create(baseUri), config, false);
server.start();
}
但是我遇到以下错误:
Exception in thread "main" org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type public java.lang.String com.mercuria.odyssey.server.GrizllyOptional$HelloWorldResource.getHello(java.util.Optional) at index 0.; source='ResourceMethod{httpMethod=GET, consumedTypes=[], producedTypes=[text/plain], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class com.mercuria.odyssey.server.GrizllyOptional$HelloWorldResource, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@a3d9978]}, definitionMethod=public java.lang.String com.mercuria.odyssey.server.GrizllyOptional$HelloWorldResource.getHello(java.util.Optional), parameters=[Parameter [type=class java.util.Optional, source=maybe, defaultValue=null]], responseType=class java.lang.String}, nameBindings=[]}']
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:555)
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:184)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:350)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:347)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:347)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:311)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.<init>(GrizzlyHttpContainer.java:337)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:140)
at com.mercuria.odyssey.server.GrizllyOptional.main(GrizllyOptional.java:33)
我想我需要做些什么,以使Jersey知道如何处理Optional
参数,但是我不知道该怎么办!
I presume I need to do something about so that Jersey knows how to handle Optional
parameters, but I've no idea what!
推荐答案
因此,允许作为@xxxParam
的参数类型,您需要满足以下要求之一:
So parameter types that are allowed as a @xxxParam
, you need to meet one of these requirements:
-
成为原始类型
Be a primitive type
具有一个接受单个String参数的构造函数
Have a constructor that accepts a single String argument
具有名为valueOf()
或fromString()
的静态方法,该方法接受单个String参数(例如,参见Integer.valueOf(String)
)
Have a static method named valueOf()
or fromString()
that accepts a single String argument (see, for example, Integer.valueOf(String)
)
具有ParamConverterProvider
JAX-RS扩展SPI的注册实现,该实现返回能够执行来自字符串"的ParamConverter
实例.类型的转换.
Have a registered implementation of ParamConverterProvider
JAX-RS extension SPI that returns a ParamConverter
instance capable of a "from string" conversion for the type.
为List<T>
,Set<T>
或SortedSet<T>
,其中T
满足以上2、3或4.产生的集合是只读的.
Be List<T>
, Set<T>
or SortedSet<T>
, where T
satisfies 2, 3 or 4 above. The resulting collection is read-only.
因此在Optional
的情况下,在列表中;这不是原始的;它没有String构造函数;它没有静态的valueOf()
或fromString()
So in this case of Optional
, going down the list; it's not a primitive; it doesn't have a String constructor; it doesn't have a static valueOf()
or fromString()
因此,基本上,剩下的唯一选择就是为其实现ParamConverter
/ParamConverterProvider
对. Dropwizard(建立在Jersey之上的框架)具有
So basically, the only option left is to implement a ParamConverter
/ParamConverterProvider
pair for it. Dropwizard (a framework built on top of Jersey) has a good implementation for it. I will post it here in case the link ever goes dead
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.ClassTypePair;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Singleton
public class OptionalParamConverterProvider implements ParamConverterProvider {
private final ServiceLocator locator;
@Inject
public OptionalParamConverterProvider(final ServiceLocator locator) {
this.locator = locator;
}
/**
* {@inheritDoc}
*/
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
if (Optional.class.equals(rawType)) {
final List<ClassTypePair> ctps = ReflectionHelper.getTypeArgumentAndClass(genericType);
final ClassTypePair ctp = (ctps.size() == 1) ? ctps.get(0) : null;
if (ctp == null || ctp.rawClass() == String.class) {
return new ParamConverter<T>() {
@Override
public T fromString(final String value) {
return rawType.cast(Optional.ofNullable(value));
}
@Override
public String toString(final T value) {
return value.toString();
}
};
}
final Set<ParamConverterProvider> converterProviders = Providers.getProviders(locator, ParamConverterProvider.class);
for (ParamConverterProvider provider : converterProviders) {
final ParamConverter<?> converter = provider.getConverter(ctp.rawClass(), ctp.type(), annotations);
if (converter != null) {
return new ParamConverter<T>() {
@Override
public T fromString(final String value) {
return rawType.cast(Optional.ofNullable(value).map(s -> converter.fromString(value)));
}
@Override
public String toString(final T value) {
return value.toString();
}
};
}
}
}
return null;
}
}
请注意,如果您使用的是Jersey 2.26+版本,则不会注入ServiceLocator
,而是会使用InjectionManager
.同样,接受locator
的参数,您将需要更改管理器.
Note, if you are using a Jersey version 2.26+, instead of injecting ServiceLocator
you will use InjectionManager
instead. Also the argument that accepts a locator
, you will need to change the the manager.
使用此类,您只需在Jersey应用程序中注册它即可.
With this class, you just need to register it with your Jersey application.
这篇关于让Jersey使用可选参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!