首页 > 解决方案 > 未从 VertxResteasyDeployment 调用 JAX-RS DynamicFeature 实现

问题描述

我正在尝试使用 MDC 将每个 http 请求的唯一请求 ID 记录到我的资源中,以便调试和跟踪特定请求。同样,我创建了一个自定义注释@TagRequestID。下面是用于记录请求的 ID。但是我无法实现请求的不是通过 DynamicFilter 实现,我认为 VertxResteasyDeployment 类中应该有一些方法或方式应该有助于解决这个问题。

基本上,即使我尝试使用 VertxResteasyDeployment 类的setProviders方法,请求也不会通过过滤器。有人可以指导我在这里缺少什么吗?我相信应该有一些配置请求通过我们注入 RequestIdConfig bean 的 DynamicFeature 实现传递请求。(假设我已经创建了 RequestIdConfig bean)。

@TagRequestID 代码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TagRequestID {
}

自定义类 RequestIdConfig :

import lombok.Data;
import javax.validation.constraints.NotNull;

@Data
public class RequestIdConfig {
 @NotNull
 private String resourcePackage;

 @NotNull
 private String requestIdHeaderKey;

 @NotNull
 private Boolean requestIdMandatoryFlag;
}

自定义类 RequestIdFeature :

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

@Slf4j
@AllArgsConstructor
@Provider
public class RequestIdFeature
 implements DynamicFeature {

 RequestIdConfig requestIdConfig;

 @Override
 public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) {
 log.info("testing now===");

 final Class<?> resourceClass = resourceInfo.getResourceClass();
 //Check if the current resource is to validated
 if (resourceClass.getPackage().getName().startsWith(requestIdConfig.getResourcePackage())) {
 //Check if the Validation annotation is present
 if (resourceInfo.getResourceMethod().getAnnotation(TagRequestID.class) != null) {
 log.info(resourceInfo.getResourceMethod() + " registered for clientID validation");
 featureContext.register(new RequestIdFilter(requestIdConfig, resourceInfo));
 }
 }
 }
}

过滤器的自定义类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.MDC;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;

@Data
@AllArgsConstructor
@Slf4j
@Provider
public class RequestIdFilter
 implements ContainerRequestFilter {

 RequestIdConfig requestIdConfig;
 ResourceInfo resourceInfo;

 public static final String REQUEST_ID = "request-Id";

 @Override
 public void filter(ContainerRequestContext containerRequestContext) throws IOException {

 log.info("ClientIdValidation filter method invoked");

 Method resourceMethod = resourceInfo.getResourceMethod();

 // Validate Clients
 validatePermissions(containerRequestContext);
 }

 private void validatePermissions(final ContainerRequestContext containerRequestContext) {
 String requestId = containerRequestContext.getHeaderString(requestIdConfig.getRequestIdHeaderKey());

 //Make sure the Header key is present if mandatory flag is true
 if (requestIdConfig.getRequestIdMandatoryFlag() && StringUtils.isAnyEmpty(requestId)) {
 throw new WebApplicationException(requestIdConfig.getRequestIdHeaderKey() + " can't be null", Response.Status.UNAUTHORIZED);
 }

 //If no request ID present, generate a UUID
 if (StringUtils.isAnyEmpty(requestId)) {
 requestId = UUID.randomUUID()
 .toString();
 }

 containerRequestContext.setProperty(REQUEST_ID, requestId);
 MDC.put(REQUEST_ID, requestId);
 }
}

资源或控制器代码:

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.vertx.core.Vertx;
import io.vertx.core.WorkerExecutor; 

public class Processor {
@POST
@TagRequestID
@Path("/update_record")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON })
public void updateEvent(String data) throws Exception{
//do something here
}

我们运行它的服务器代码:

import mypackage.Processor;
import io.vertx.core.AbstractVerticle;
import org.jboss.resteasy.plugins.server.vertx.VertxRequestHandler;
import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment;
import org.springframework.context.ApplicationContext;


public class VertxServer extends AbstractVerticle {
    VertxServer(final ApplicationContext context) {
    }

    @Override
    public void start() throws Exception {
        VertxResteasyDeployment deployment = new VertxResteasyDeployment();
        deployment.start();
        deployment.getRegistry().addPerInstanceResource(Processors.class);
        vertx.createHttpServer()
                .requestHandler(new VertxRequestHandler(vertx, deployment))
                .listen(8080);
    }
}

一旦服务器启动并运行,然后在上面的控制器上同时点击两个请求。IE :

curl -X POST \ http://localhost:8080/v1/update_record \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \
-H 'Postman-Token: c9494189- 4ac9-9f6c-44f6-216186c74431'\-d'{"id":"123"}'

标签: javajax-rsresteasyvert.xmdc

解决方案


在注册资源之前,在VertxServer.java中在 providerFactory中注册您的 dynamicFeature 实例。在 providerFactory 中注册的这个 dynamicFeatures 和过滤器将在注册资源时使用。

您的代码将如下所示:

VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
deployment.getProviderFactory().register(new RequestIdFeature(getRequiredBean());
deployment.getRegistry().addPerInstanceResource(Processors.class);
vertx.createHttpServer()
            .requestHandler(new VertxRequestHandler(vertx, deployment))
            .listen(8080);

推荐阅读