我有一个
ContainerRequestFilter
,它为我的 JAX-RS/Quarkus 应用程序中调用的每个端点收集一些指标。这需要访问每个资源上的 @Path
注释中指定的 URI 模式(“/api/users/{userId}”,而不是像“/api/users/1711”这样的已解析 URL)
我使用以下代码,该代码对于简单资源非常有效,但对于嵌套资源则失败:
class MyFilter implements ContainerRequestFilter {
@Context
ResourceInfo resourceInfo
...
private String getUri() {
var uriBuilder = UriBuilder.fromResource(resourceInfo.getResourceClass());
var pathAnnotationOnMethod = resourceInfo.getResourceMethod().getAnnotation(Path.class);
if (pathAnnotationOnMethod != null) {
uriBuilder = uriBuilder.path(resourceInfo.getResourceMethod());
}
return uriBuilder.toTemplate();
}
}
此方法不适用于如下所示的嵌套资源:
@Path("api/users")
class UsersResource {
@Inject
UserResource userResource;
@Inject
UserAccessService userAccessService;
@Context
ResourceContext resourceContext;
@Path("{id}")
public UserResource user(@PathParam("id") long userId) {
userAccessService.ensureAccessTo(userId);
return resourceContext.initResource(userResource);
}
}
@RequestScoped
class UserResource {
@GET
public Response getUser(@PathParam("id") long userId) {
...
}
}
它抛出一个异常,抱怨 UserResource
没有
@Path
注释。所以我的问题是:如何解析嵌套(子)资源的 URL 模式?
/**
* Quarkus (or RESTEasy) doesn't make it easy to get the URI template including the
* subresource. This custom class is used to track which subresource were invoked on
* our way to the endpoint.
*/
@RequestScoped
public class SubresourcesTracked {
private UriBuilder uriBuilder = null;
public Optional<UriBuilder> getUriBuilder() {
return Optional.ofNullable(uriBuilder);
}
public UriBuilder computeUriBuilderIfAbsent(Supplier<UriBuilder> supplier) {
if (uriBuilder == null) {
uriBuilder = supplier.get();
}
return uriBuilder;
}
}
@InterceptorBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TrackingSubresource {
}
/**
* Quarkus (or RESTEasy) doesn't make it easy to get the URI template including the
* subresource. This custom class is used to track which subresource were invoked on
* our way to the endpoint.
*/
@Interceptor
@TrackingSubresource
public class TrackingSubresourceInterceptor {
@Inject
SubresourcesTracked subresourcesTracked;
@AroundInvoke
public Object aroundInvoke(InvocationContext context) throws Exception {
var method = context.getMethod();
var uriBuilder = subresourcesTracked.computeUriBuilderIfAbsent(() -> {
var resource = method.getDeclaringClass();
Path pathAnnotationOnResource = resource.getAnnotation(Path.class);
return UriBuilder.fromPath(pathAnnotationOnResource.value());
});
var pathAnnotationOnMethod = method.getAnnotation(Path.class);
if (pathAnnotationOnMethod != null) {
uriBuilder.path(pathAnnotationOnMethod.value());
}
return context.proceed();
}
}
在请求过滤器中:
@Inject
SubresourcesTracked subresourcesTracked;
private String getUri() {
var uriBuilder = subresourcesTracked.getUriBuilder().map(UriBuilder::toTemplate).map(UriBuilder::fromPath).orElseGet(() -> UriBuilder.fromResource(resourceInfo.getResourceClass()));
var path = resourceInfo.getResourceMethod().getAnnotation(Path.class);
if (path != null) {
uriBuilder = uriBuilder.path(resourceInfo.getResourceMethod());
}
return uriBuilder.toTemplate();
}