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



我有一个使用Swagger API的项目,其服务器代码是由Swagger-codegen-2.4.24为jaxr语言生成的。

生成的代码具有一个后缀为"的抽象类*ApiService";定义了一系列方法,每个方法对应于API Swagger规范中定义的每个操作。

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

现在,在我的自定义类上*ApiService";,显然有javax.ws.rs.core.SecurityContext类的局部变量,我需要获取请求头的值"X-Forwarded-For";。

如果我调试我的自定义类,我会看到SecurityContext接口是org.lassfish.jersey.server.internal.prrocess.SecurityContextInjectee的一个实例,它有我需要的头。

既然SecurityContextInjectee是私有的,我就无法使用它,我该如何获得这些信息?

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

期待您的评论。

在每个规范版本中,您都可以定义一个header,就像一个可能的参数位置一样。

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

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"
}
]

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

有一个开放的Github问题询问您所描述的行为,以通用的方式处理头处理。

在这个相关的SO答案中也建议了一个合适的选项,可以修改API代码生成中使用的Mustache模板,并在其中包含所需的头处理。请注意,这将降低代码的可维护性,并且您将面临执行某些更改的风险,这些更改会破坏与官方Swagger Codegen存储库的兼容性。我不确定Swagger Codegen,但在OpenAPI生成器中,有一个选项可以覆盖使用的模板,而无需修改官方发行版中提供的实际模板。请参阅此相关SO问题。

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

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;
}

最后,另一种可能是使用一个过滤器来处理您的头。如果需要,可以使用例如线程本地变量将头值传递给底层服务。这个想法大致如下。

首先,定义一个方便的对象来包装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请求中接收的信息中读取标题:

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本身;另一方面,该解决方案假设过滤器将在线程本地变量中为底层服务的每个请求提供正确的头,换句话说,不存在任何与线程相关的问题。我认为应该是这样,但这是需要测试的。

最新更新