如何在 Swagger Codegen 生成的 JAXRS 类 上访问请求 headers

How to access request headers on JAXRS classes generated by Swagger Codegen

我有一个带有 Swagger API 的项目,它的服务器代码是由 swagger-codegen-2.4.24 为语言 jaxrs 生成的。

生成的代码有一个抽象的class后缀为“*ApiService”,定义了一系列方法,每个方法对应API.[=12=的Swagger规范上定义的每个操作。 ]

每个方法都有一个javax.ws.rs.core.SecurityContext接口局部变量。

现在,在扩展“*ApiService”的自定义 class 上,显然有 javax.ws.rs.core.SecurityContext class 局部变量,我需要获取请求的值 header “X-Forwarded-For”.

如果我调试自定义 class,我会看到 SecurityContext 接口是 org.glassfish.jersey.server.internal.process.SecurityContextInjectee 的一个实例,它具有我需要的 header。

我如何获取该信息,因为我无法使用 SecurityContextInjectee,因为它是私有的?

我意识到,如果 swagger-codegen 生成的 classes 添加 javax.servlet.http.HttpServletRequest class,除了 SecurityContext 之外,还可以访问请求参数,但是我没有看到任何允许这样做的 jaxrs 参数。

期待您的意见。

在每个规范版本中,您都可以定义一个 header,例如可能的 parameter locations

因此,一种可能的解决方案是在请求 parameters 部分所需的方法中定义 header:

parameters:
    -
        name: X-Forwarded-For
        description: X-Formarwed-For header.
        schema:
            type: string
        in: header

或者,在 JSON 表示法中:

    "parameters": [
        {
            "name": "X-Forwarded-For",
            "description": "X-Formarwed-For header.",
            "schema": {
                "type": "string"
            },
            "in": "header"
        }
    ]

我知道这可能是一个不太可维护的解决方案,因为您需要在每个请求中包含 header,但也许您可以通过在服务实现中继承来缓解这一事实。

有一个开放的 Github issue 要求您描述的行为,以一般方式处理 header 处理。

this related SO answer, could be modifying the Mustache templates used in the API code generation and include within them the required headers processing. Please, be aware that this will do your code less maintainable and you will have the risk of perform some change that breaks the compatibility with the official Swagger Codegen repository. I am not sure in Swagger Codegen, but in the OpenAPI generator there is an option to override the used templates without modifying the actual provided in the official distribution. Please, see this related SO question.

中也建议了一个合适的选项

虽然看起来情况不再如此,但至少在 class 是 public 的旧版本 Jersey 中,您可以尝试访问 requestContext 内部变量org.glassfish.jersey.server.internal.process.SecurityContextInjectee 也通过反思,尽管我认为该解决方法使您的应用程序非常依赖于实现。在任何情况下,也许您可​​以定义一个这样的实用方法,您可以在您的服务实现中重用它:

public static String getXForwardedForHeaderValue(final SecurityContext securityContext) {
  SecurityContextInjectee securityContextImpl = (SecurityContextInjectee) securityContext;
  Field requestContextField = SecurityContextInjectee.class.getDeclaredField("requestContext");
  requestContextField.setAccessible(true);
  ContainerRequestContext requestContext = requestContextField.get(securityContextImpl);
  String xForwardedForHeaderValue = requestContext.getHeaderString("X-Forwarded-For");
  return xForwardedForHeaderValue;
}

最后,另一种可能性是使用 filter 来处理您的 header。如果需要,您可以使用例如线程局部变量将 header 值传递给基础服务。这个想法类似于以下内容。

首先,定义一个方便的 object 来包装您的 ThreadLocal 值:

public class XForwardedForHeaderHolder{

    private static final ThreadLocal<String> value = new ThreadLocal<String>();

    public static void setXForwardedForHeader(String xForwardedFor) {
        value.set(xForwardedFor);
    }

    public static String getXForwardedForHeader() {
        return value.get();
    }

    public static void clean() {
        value.remove();
    }
}

接下来,创建一个 ContainerRequestFilter。此过滤器将从正在处理的 HTTP 请求中收到的信息中读取 header:

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
 
@Provider
public class XForwardedForHeaderRequestFilter implements ContainerRequestFilter {
 
    @Override
    public void filter(ContainerRequestContext requestContext)
        throws IOException {
 
        String xForwardedForHeaderValue = requestContext.getHeaderString("X-Forwarded-For");
        XForwardedForHeaderHolder.setXForwardedForHeader(
            xForwardedForHeaderValue
        );
    }
}

最后,在您的服务实施中使用价值:

String xForwardedForHeaderValue = XForwardedForHeaderHolder.getXForwardedForHeader();
// Clean up
XForwardedForHeaderHolder.clean();

提醒一句:一方面,过滤器注册应该可以正常工作,但这可能取决于您使用的 JAXRS 版本和 Swagger 本身;另一方面,该解决方案假设过滤器将在线程局部变量中为对底层服务的每个请求提供正确的 header,换句话说,不存在任何与线程相关的问题。我觉得应该是这样,但这是需要检验的东西。