micronaut-core
micronaut-core copied to clipboard
Http client header does not support placeholders on class level
Expected Behavior
I found this issue developing workaround for http client with localhost domain (e.g. my-app.localhost) which works normally as it is handled by nginx ingress within Kubernetes but is not recognized as a valid domain by Micronaut. Back to issue itself.
Client should support placeholders for headers at class level for declarative clients the same way as you can pass url. Header placeholders work only on request level.
Actual Behaviour
12:36:14.002 [main] ERROR i.m.r.intercept.RecoveryInterceptor - Type [com.aptitudesoftware.authentication.KeycloakClient$Intercepted] executed with error: Could not resolve placeholder ${keycloak.host}
io.micronaut.context.exceptions.ConfigurationException: Could not resolve placeholder ${keycloak.host}
at io.micronaut.context.env.DefaultPropertyPlaceholderResolver$PlaceholderSegment.getValue(DefaultPropertyPlaceholderResolver.java:297)
at io.micronaut.context.env.DefaultPropertyPlaceholderResolver.resolveRequiredPlaceholders(DefaultPropertyPlaceholderResolver.java:106)
at io.micronaut.inject.annotation.EnvironmentAnnotationValue.lambda$new$2(EnvironmentAnnotationValue.java:52)
at io.micronaut.core.annotation.AnnotationValue.getRawSingleValue(AnnotationValue.java:1492)
at io.micronaut.core.annotation.AnnotationValue.stringValue(AnnotationValue.java:860)
at io.micronaut.core.annotation.AnnotationValue.stringValue(AnnotationValue.java:892)
at io.micronaut.http.client.bind.binders.HeaderClientRequestBinder.bind(HeaderClientRequestBinder.java:43)
at io.micronaut.http.client.interceptor.HttpClientIntroductionAdvice.lambda$intercept$2(HttpClientIntroductionAdvice.java:214)
at java.base/java.util.Optional.ifPresent(Optional.java:178)
at io.micronaut.http.client.interceptor.HttpClientIntroductionAdvice.intercept(HttpClientIntroductionAdvice.java:214)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:137)
at io.micronaut.retry.intercept.RecoveryInterceptor.intercept(RecoveryInterceptor.java:92)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:137)
at com.aptitudesoftware.authentication.KeycloakClient$Intercepted.getToken(Unknown Source)
at com.aptitudesoftware.authentication.IdTokenManager.getIdToken(IdTokenManager.java:24)
Steps To Reproduce
@Client("${keycloak.url}")
@Headers(
@Header(name = HttpHeaders.HOST, value = "${keycloak.host}")
)
@Introspected
public interface KeycloakClient {
@Post(uri = "/realms/${keycloak.realm}/protocol/openid-connect/token", produces = MediaType.APPLICATION_FORM_URLENCODED, consumes = MediaType.APPLICATION_JSON)
AccessToken getToken(@Body Map tokenRequest);
}
@ConfigurationProperties(KeycloakConfiguration.PREFIX)
@Data
@Introspected
public class KeycloakConfiguration {
public static final String PREFIX = "keycloak";
private URL url;
private String host;
private String realm;
private AuthenticationMethod method;
@SneakyThrows
public void setUrl(URL url) {
if (url.getHost().endsWith("localhost")) {
this.url = new URL(url.getProtocol(), "localhost", url.getPort(), url.getFile());
this.host = url.getHost();
} else {
this.url = url;
}
}
}
Environment Information
Windows 11 JDK 17
Example Application
No response
Version
3.6.1