In every specification version you can define a header
like one of the possible parameter locations.
So, one possible solution, will be to define the header in the methods you required in the request parameters
sections:
parameters:
-
name: X-Forwarded-For
description: X-Formarwed-For header.
schema:
type: string
in: header
Or, in JSON notation:
"parameters": [
{
"name": "X-Forwarded-For",
"description": "X-Formarwed-For header.",
"schema": {
"type": "string"
},
"in": "header"
}
]
I am aware that perhaps it is a less maintainable solution because you will need to include the header in every request, but maybe you could mitigate that fact with inheritance in your services implementation.
There is an open Github issue asking for the behavior you described, handling the header processing in a general way.
One suitable option, suggested as well in 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.
Although it seems that is no longer the case, at least in older versions of Jersey in which the class was public
, you could try accessing the requestContext
internal variable in org.glassfish.jersey.server.internal.process.SecurityContextInjectee
by reflection as well, although I think that workaround makes your application very implementation dependent. In any case, perhaps you could define an utility method like this that you could reuse in your services implementation:
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;
}
Finally, another possibility could be using a filter that process your header. If required you could pass the header value using for instance a thread local variable to the underlying services. The idea would be something like the following.
First, define a convenient object that wraps your ThreadLocal
value:
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();
}
}
Next, create a ContainerRequestFilter
. This filter will read header from the information received in the HTTP request being processed:
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
);
}
}
Finally, consume the value in your services implementation:
String xForwardedForHeaderValue = XForwardedForHeaderHolder.getXForwardedForHeader();
// Clean up
XForwardedForHeaderHolder.clean();
A word of caution: on one hand, the filter registration should work properly but it could depend on the JAXRS version you are using and Swagger itself; on the other, the solution assume that the filter will provide, in the thread local variable, the right header for every request to the underlying services, in other words, that there are not any threading related issue. I think it should be the case, but it is something that need to be tested.