3

I am using spring webclient to make a Facebook graph api request with url containing {comment_count}

But, getting this exception

java.lang.IllegalArgumentException: Not enough variable values available to expand reactive spring

Code Snippet :

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

@Component
public class Stackoverflow {

    WebClient client = WebClient.create();

    public Mono<Post> fetchPost(String url) {
        // Url contains "comments{comment_count}"
        return client.get().uri(url).retrieve()
                .bodyToMono(Post.class);
    }
}

I know the solution with resttemplate, But i need to use spring webclient.

kaushik
  • 556
  • 1
  • 7
  • 22
  • You aren't passing in anything to replace `{comment_count}`. Hence the error. – M. Deinum Oct 01 '19 at 07:02
  • 1
    That is not a variable, Url itself contains curly braces. It is a graph api – kaushik Oct 01 '19 at 07:15
  • An URL containing `{name}` that is has a template variable and needs to be replaced. This both goes for the `RestTemplate` as well as the `WebClient`. It is used a a template variable and as such needs to be assigned a value, at least that is what those classes expect it to be. – M. Deinum Oct 01 '19 at 07:16
  • What if endpoint itself contains braces, i am trying for this url : https://graph.facebook.com/v3.2/PAGE_ID/posts?fields=comments{comment_count}&access_token=ACCESS_TOKEN – kaushik Oct 01 '19 at 07:20
  • Then you need to construct a `WebClient` with a properly configured `UriBuilderFactory`. Basically the underlying infrastructure for parsing URL and template is exactly the same as for a `RestTemplate`. – M. Deinum Oct 01 '19 at 07:35
  • @M.Deinum Can you show to construct a Webclient with an example for the above url – kaushik Oct 01 '19 at 07:53
  • Use `WebClient.Builder` to assign a proper `UriBuilderFactory` which does what you want. – M. Deinum Oct 01 '19 at 08:21

2 Answers2

6

You can create your URL using UriComponentsBuilder as follows

 webClient.get().uri(getFacebookGraphURI(3)).retrieve().bodyToMono(Object.class);

private URI getFacebookGraphURI(int comments){
   return UriComponentsBuilder.fromHttpUrl("https://graph.facebook.com")
        .pathSegment("v3.2", "PAGE_ID", "posts").queryParam("fields", "comments{comment_count}")
        .queryParam("access_token", "acacaaac").build(comments);

  }

OR

int commentsCount = 3; webClient.get().uri(UriComponentsBuilder.fromHttpUrl("https://graph.facebook.com") .pathSegment("v3.2", "PAGE_ID", "posts").queryParam("fields", "comments{comment_count}") .queryParam("access_token", "acacaaac").build().toString(),commentsCount).retrieve().bodyToMono(Object.class);

yash sugandh
  • 608
  • 4
  • 6
0

The solution I use is to disable the encoding in DefaultUriBuilderFactory

val urlBuilderFactory = DefaultUriBuilderFactory("https://foo.bar.com").apply {
    encodingMode = EncodingMode.NONE
}

val wc = wcb
    .clone()
    .uriBuilderFactory(urlBuilderFactory)
    .build()

It's in Kotlin, in Java you just have to use DefaultUriBuilderFactory#setEncodingMode(EncodingMode) with NONE as parameter.

Due to this change of behavior, you have to encode your query params yourself. To do so, I use the

val query = URLEncoder.encode(query_as_string, StandardCharsets.UTF_8.toString())

And I can perform call like this:

wc.get()
    .uri { it
        .path(graphqlEndpoints)
        .queryParam("variables", query)
        .build()
    }
    .retrieve()
    .bodyToFlux<String>()
    // ...

For Java (as per comment by Anurag below) it will be something like this:

var uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);   
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);

var wc = WebClient.builder()
    .baseUrl(baseUrl) 
    .uriBuilderFactory(uriBuilderFactory)
    // ... other options
    .build(); 

wc.get()
     .uri(uriBuilder -> uriBuilder
         .path(path)
         // value still needs to be URL-encoded as needed (e.g., if value is a JSON
         .queryParam("param", value))
         .build())
     .retrieve()
     .bodyToMono(...);

As noted above, you will still need to URL-encode the parameter value. With this approach, you are merely avoiding "double URL encoding" by replacing the urlBuilderFactory.

Dexter Legaspi
  • 3,192
  • 1
  • 35
  • 26
Kevin Davin
  • 511
  • 1
  • 4
  • 12
  • for java this worked for me- DefaultUriBuilderFactory uriBuilderFactory= new DefaultUriBuilderFactory("http://example.com"); uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); WebClient webClient = WebClient.builder() .clientConnector(connector()) .filter(logRequest()) .baseUrl("http://example.com") .uriBuilderFactory(uriBuilderFactory) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); – Anurag Yadav Feb 04 '22 at 11:20