micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6k stars 1.04k forks source link

Multiple client filters in Micronaut #10881

Closed BharathMC closed 4 weeks ago

BharathMC commented 1 month ago

Issue description

I have a micronaut service which has two filters with @Named annotation set to unique values. Without @Named annotation on HttpClient, filters work fine. But, with @Named annotation, it does not work. In simple @ClientFilters class definition is applied to all the client requests globally.

Requirement is to have multiple ClientFilters classes and each HttpClient injection should be associated to only specific filter definition.

Note: URL Pattern matching is one option for each filter, but in this case URL pattern is not known or dynamic.

Thanks

So far, I tried this and filter class is not getting applied when I use @Named annotation in HttpClient class as shown in below code snippets

@ClientFilter(Filter.MATCH_ALL_PATTERN)
@Named("MyHttpClient1")
public class ClientInterceptor1 {

    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequest(MutableHttpRequest<?> request) {
      // some logic    
    }
}
@ClientFilter(Filter.MATCH_ALL_PATTERN)
@Named("MyHttpClient2")
public class ClientInterceptor2 {

    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequest(MutableHttpRequest<?> request) {
      // some logic    
    }
}

Bean to provide the HttpClient Configuration

@Factory
public class HttpClientFactory {
    @Bean
    @Named("MyHttpClient1")
    public HttpClient myLocalhttpClient(HttpClientConfiguration httpClientConfiguration) throws   MalformedURLException {
        return HttpClient.create(new URL("http://localhost"), httpClientConfiguration);
    }
}

And a service class

@Controller("/test")
@RequestScope
public class TestResource {

    @Inject
    @Named("MyHttpClient1")
    HttpClient httpClient;

    @Get(uri = "/http")
    public HttpResponse<?> testHttpClient() {
        ...
        HttpResponse<String> response = httpClient.toBlocking().exchange(request, String.class);
        ...
        return HttpResponse.ok();
    }
}
graemerocher commented 1 month ago

what you are trying to do is not supported. See https://docs.micronaut.io/4.4.10/guide/#clientFilter section "Filter Matching By Annotation" on how to achieve what you are trying to achieve

BharathMC commented 1 month ago

Hi @graemerocher,

Thanks for the response.

I have went through the documentation already. Not supported meaning: client filters are applied to ALL outgoing requests invoked via HttpClient instance and there is no way to avoid that, is that right ?

Thanks

graemerocher commented 1 month ago

you are creating the client yourself manually, so that is bypassing any filters we would usually inject

BharathMC commented 1 month ago

We have Springboot based application microservices and it supports specific filter/interceptor association to RestTemplate (http client) bean.

Can I create a feature request for this to support in micronaut ?

graemerocher commented 1 month ago

I am failing to see from your example why you need to explicitly create the client and why the section "Filter Matching By Annotation" on the documentation does not meet your requirement. Can you explain why?

BharathMC commented 1 month ago

Reason why client filter @ClientFilter(Filter.MATCH_ALL_PATTERN) with pattern matching cannot be used:

We have created a micronaut library which will be used by micronaut application microservices. So, within library, we have created filters which does some business operations for every outgoing request.

Now, when the enduser application uses our library, they will have some outgoing requests other than the one which needs filter and some requets which does not need filters to be applied. Since micronaut applies filter for all the requests, our library filters will be applied for all of it. Which is not intended.

That's where the requirement of HttpClient with different interceptors/filters association requirement comes in. If micronaut supports associating the HttpClient with respective filter, we can ask enduser to use our HttpClient bean created in library and for other uses, use default HttpClient

Now, coming to pattern matching, as this is library, we cannot restrict with a URL pattern hardcoding in library.

If you think of any other way to achieve this is micronaut, please let me know.

Thanks.

graemerocher commented 4 weeks ago

Right but why can't your customer define in configuration:

micronaut.http.services.client1.url=http://localhost

Then filter:

@ClientFilter(serviceId="client1")
public class ClientInterceptor1 {

    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequest(MutableHttpRequest<?> request) {
      // some logic    
    }
}

Then inject:

@Inject @Client("client1") HttpClient client;
BharathMC commented 4 weeks ago

Hi @graemerocher,

I think the solution provided perfectly fits our requirement. Thank you on that!

Regards, Bharath