有jersey-2动态构建端点RESTful资源。 路径方法
resourceBuilder.path("item/{id}");
为特定资源定义匹配的url模式。例如。当请求www.myapp.com/api/item/22资源时,具有上述路径模式的处理程序将处理请求。
我需要找出请求呼叫将匹配的路径模式(此处为' s" item / {id}")。
getMatchedResources()或getMatchedURIs()不提供路径模式列表。
methodBuilder.produces(new MediaType("text", "plain"))
.handledBy(new Inflector<ContainerRequestContext, String>() {
@Override
public String apply(ContainerRequestContext ctx) {
List<Object> resources = ctx.getUriInfo().getMatchedResources();
List<String> uris = ctx.getUriInfo().getMatchedURIs();
return "Programmatically generated endpoint";
}
});
有没有人解决类似的问题?
答案 0 :(得分:1)
由于您动态生成端点,因此可以通过多种方式执行此操作。这是实现同样目标的两种方法。
第一种方法:
创建自定义Inflector:
// Special type of Inflector that's aware of the resource path. Users extending this class implement the
// apply(data, resourcePath) method. Internally it uses a custom resource registry to get the resource path.
public static abstract class PathAwareInflector<RESULT> implements Inflector<ContainerRequestContext, RESULT> {
@Inject
private ResourceRegistry resourceRegistry;
private String resourcePath;
@Override
public final RESULT apply(ContainerRequestContext data) {
return apply(data, getResourcePath(data));
}
public abstract RESULT apply(ContainerRequestContext data, String resourcePath);
private String getResourcePath(ContainerRequestContext data) {
if (resourcePath == null) {
resourcePath = resourceRegistry.forPath(data.getUriInfo().getPath(true))
.stream()
.findFirst()
.map(Resource::getPath)
.orElse("UNKNOWN");
}
return resourcePath;
}
}
然后您可以注册您的资源:
final Resource.Builder resourceBuilder = Resource.builder("/item/{id}");
final ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod("GET");
methodBuilder.produces(MediaType.TEXT_PLAIN_TYPE)
.handledBy(new PathAwareInflector<String>() {
@Override
public String apply(ContainerRequestContext context, String resourcePath) {
return resourcePath + "\n";
}
});
registerResources(resourceBuilder.build());
第二种方法
使用自定义注释@ResourcePath
,您可以像这样定义方法:
// Simple Endpoint with one method. Uses special custom @ResourcePath annotation to inject the resource path in two ways:
// As method argument, and as field using the same annotation. In a real world scenario you'd user either or.
public static class Endpoint {
@ResourcePath
String resourcePath2;
public String endpoint(ContainerRequestContext context, @ResourcePath String resourcePath) {
return resourcePath2 + " - " + resourcePath + "\n";
}
}
你注册就是这样:
// This resource is configured through a class method. While the path, method, and produces are defined here,
// the Endpoint.endpoint method actually handles the resource, which class and parameters can be annotated.
final Resource.Builder resourceBuilder1 = Resource.builder("/annotation/{id}");
final ResourceMethod.Builder methodBuilder1 = resourceBuilder1.addMethod("GET");
methodBuilder1.produces(MediaType.TEXT_PLAIN_TYPE)
.handledBy(Endpoint.class, Endpoint.class.getMethod("endpoint", ContainerRequestContext.class, String.class));
registerResources(resourceBuilder1.build());
要支持这种机制,您还需要有这些类:
// Custom annotation used by ResourcePathInjectionResolver and ResourcePathValueFactoryProvider to
// inject field and parameters annotated with this annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface ResourcePath {
}
// It registers providers/injection resolvers/factories to enable @ResourcePath and PathAwareInflector
public static class PathResourceFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new ResourceRegistry.Binder());
context.register(ResourceRegistry.Processor.class);
context.register(new ResourcePathInjectionResolver.Binder());
context.register(ResourcePathValueFactoryProvider.class);
return true;
}
}
// Custom injector that resolves @ResourcePath annotations for field injection. It delegates to
// ResourcePathValueFactoryProvider extending a ParamInjectionResolver.
public static class ResourcePathInjectionResolver extends ParamInjectionResolver<ResourcePath> {
static final class Binder extends AbstractBinder {
@Override
protected void configure() {
bind(ResourcePathInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<ResourcePath>>() {
})
.in(Singleton.class);
}
}
public ResourcePathInjectionResolver() {
super(ResourcePathValueFactoryProvider.class);
}
}
// Custom Factory Provider that resolves @ResourcePath annotations for parameter injection. It
// uses a custom Resource Registry to find the actual path.
public static class ResourcePathValueFactoryProvider extends AbstractValueFactoryProvider {
public static class ResourcePathValueFactory extends AbstractContainerRequestValueFactory<String> {
@Inject
private ResourceRegistry resourceRegistry;
public ResourcePathValueFactory() {
}
@Override
public String provide() {
return resourceRegistry.forPath(getContainerRequest().getPath(true))
.stream()
.findFirst()
.map(Resource::getPath)
.orElse(null);
}
}
@Inject
public ResourcePathValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
super(mpep, locator, Parameter.Source.values());
}
@Override
protected Factory<?> createValueFactory(Parameter parameter) {
if (parameter.isAnnotationPresent(ResourcePath.class)) {
return new ResourcePathValueFactory();
}
return null;
}
}
// Resource registry that stores the Resource Model and its configuration. It is bootstrapped with custom ModelProcessor
// that simply stores the resource model into the registry.
public static class ResourceRegistry {
private ResourceModel resourceModel;
private void setResourceModel(ResourceModel resourceModel) {
this.resourceModel = resourceModel;
}
protected List<Resource> forPath(String path) {
return resourceModel.getResources()
.stream()
.filter(r -> r.getPathPattern().match("/" + path) != null)
.collect(Collectors.toList());
}
static final class Binder extends AbstractBinder {
@Override
protected void configure() {
bind(ResourceRegistry.class)
.to(ResourceRegistry.class)
.in(Singleton.class);
}
}
public static class Processor implements ModelProcessor {
private ResourceRegistry resourceRegistry;
@Inject
public Processor(ResourceRegistry resourceRegistry) {
this.resourceRegistry = resourceRegistry;
}
@Override
public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) {
resourceModel.getResources().stream().forEach(r -> {
System.out.println("Path: " + r.getPath());
System.out.println("Regex: " + r.getPathPattern().getRegex());
});
resourceRegistry.setResourceModel(resourceModel);
return resourceModel;
}
@Override
public ResourceModel processSubResource(ResourceModel subResourceModel, Configuration configuration) {
return subResourceModel;
}
}
}
在您的配置中注册功能:
//Registers the Path Resource Feature that enables the functionality.
register(PathResourceFeature.class);
答案 1 :(得分:1)
我能够从请求的上下文中检索匹配的模板,方法是将其转换为ContainerRequest,然后调用getMatchedTemplates()方法。
methodBuilder.produces(new MediaType("text", "plain"))
.handledBy(new Inflector<ContainerRequestContext, String>() {
@Override
public String apply(ContainerRequestContext ctx) {
List<UriTemplate> uriTemplates = ((ContainerRequest) requestContext).getUriInfo().getMatchedTemplates();
if(uriTemplates != null && uriTemplates.size() > 0)
{
UriTemplate uriTemplate = uriTemplates.get(0);
String pathTemplate = uriTemplate.getTemplate();
//pathTemplate == "/item/{id}"
}
return "Programmatically generated endpoint";
}
});