micronaut-core icon indicating copy to clipboard operation
micronaut-core copied to clipboard

Http client header does not support placeholders on class level

Open ksawerykarwacki opened this issue 2 years ago • 0 comments

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

ksawerykarwacki avatar Sep 05 '22 10:09 ksawerykarwacki