首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从请求上下文中获取匹配模板的url

如何从请求上下文中获取匹配模板的url
EN

Stack Overflow用户
提问于 2016-03-25 02:47:21
回答 2查看 2.8K关注 0票数 1

有jersey-2动态构建端点RESTful资源。路径方法

代码语言:javascript
复制
resourceBuilder.path("item/{id}");

为特定资源定义匹配的url模式。例如,当请求www.myapp.com/api/item/22资源时,具有上述路径模式的处理程序将处理请求。

我需要找出请求调用将匹配哪个路径模式(这里是"item/{id}")。

getMatchedResources()或getMatchedURIs()不提供路径模式列表。

代码语言:javascript
复制
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";
    }
});

有没有人解决过类似的问题?

EN

回答 2

Stack Overflow用户

发布于 2016-03-25 02:53:30

由于您是动态生成端点的,因此可以通过几种方式完成此操作。这是实现相同目标的两种方法。

First方法:

创建自定义导向器:

代码语言:javascript
复制
    // 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;
        }
    }

然后你可以像这样注册你的资源:

代码语言:javascript
复制
    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,您可以这样定义您的方法:

代码语言:javascript
复制
// 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";
    }
}

你可以这样注册它:

代码语言:javascript
复制
    // 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());

为了支持这种机制,你还需要有这样的类:

代码语言:javascript
复制
// 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;
        }
    }
}

并在您的配置中注册该功能:

代码语言:javascript
复制
    //Registers the Path Resource Feature that enables the functionality.
    register(PathResourceFeature.class);
票数 1
EN

Stack Overflow用户

发布于 2016-03-25 03:51:19

我能够从请求上下文中检索匹配的模板,方法是将其转换为ContainerRequest,然后调用getMatchedTemplates()方法。

代码语言:javascript
复制
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";
   }
});
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36207816

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档