The following has been observed in spring-cloud-openfeign when using request parameter name that starts with $ sign. It's been caused by upgrading to version 10.x.
The issue has been reported here https://github.com/spring-cloud/spring-cloud-openfeign/issues/109.
public interface Client {
@GetMapping("/api")
Result api(@RequestParam("$filter") String filter);
}
Calling client.api("123") used to work in any 2.0.x release even 2.1.0.RC2 was fine. Java call was translated into
GET /api?%24filter=123 HTTP/1.1
After upgrading to 2.1.0.RELEASE or 2.1.0.RC3 it looks like this
GET /api?%24filter=%7B%24filter%7D HTTP/1.1
The {$filter} query param is passed correctly to Feign, but there as it's being converted into a feign.template.Expressions type feign.template.TemplateChunk, it does not pass the following pattern matching filter:
static {
expressions = new LinkedHashMap<>();
expressions.put(Pattern.compile("\\+(\\w[-\\w.]*[ ]*)(:(.+))?"), ReservedExpression.class);
expressions.put(Pattern.compile("(\\w[-\\w.]*[ ]*)(:(.+))?"), SimpleExpression.class);
}
Optional<Entry<Pattern, Class<? extends Expression>>> matchedExpressionEntry =
expressions.entrySet()
.stream()
.filter(entry -> entry.getKey().matcher(expression).matches())
.findFirst();
This is an odd case. I suspect that because $ is a reserved character on a the URI, feign will try to encode it before proceeding. However, $ is not allowed in a Simple Expression per RFC 6570. This will require some research.
It might be useful to mention the use case.
OData 4 specifies that "Clients that want to work with 4.0 services MUST use lower case names and specify the $ prefix".
OData service we are integrating with only supports $-prefixed parameters ($filter, $select, $top etc), so for now we cannot upgrade to Feign 10.
It is Windchill REST API that is using parameters such as $filter. There's probably OData involved as @alexanderabramov has said already.
@ojecborec
I've looked into this further and the root cause of your issue is that Feign expects URI template expressions to validate against RFC 6570 template expressions. Expressions that start with a $ are not allowed. This is why the expression is being passed over and treated as a literal.
This can be worked around by specifying the templated value in the URI. Below is an example using Feign core:
public interface Client {
@RequestLine("GET /api?$filter={filter}")
Result api(@Param(filter) String filter);
}
By directly declaring the variable name in the URI, the variable name no longer needs to have the $ character. I have not attempted this with Spring Cloud, but I suspect the following may also work:
public interface Client {
@GetMapping("/api?$filter={filter}")
Result api(@RequestParam(filter) String filter);
}
Can you give this a try?
This workaround works as expected. Although as the list of parameters gets bigger it's better to keep everything in one place, i.e. method parameters.
Another way how to provide parameters is a Map. Client definition would be
public interface Client {
@GetMapping("/api")
Result api(@RequestParam Map<String, String> parameters);
}
And you can call it
Map<String, String> parameters = new HashMap<>();
parameters.put("$filter", "123");
parameters.put("$select", "123");
client.api(parameters);
Funny thing is that this time it works without workdaround and the $ character in the parameter name is not a problem.
@ojecborec
The reason for this is that, when parameters are provided using a Map, the keys and values in contained within are treated as literals. Expressions, either as Map keys or Map values are not supported.
Thanks for bringing that to our attention. Would you be willing to help us by updating the README with your findings?
I've looked into this further and the root cause of your issue is that Feign expects URI template expressions to validate against RFC 6570 template expressions. Expressions that start with a
$are not allowed. This is why the expression is being passed over and treated as a literal.
I might have missed something, but does RFC 6570 not allow for pct-encoded values? Because I still had this issue when using "%24filter".
Using a Map still works, of course, so that's the workaround I ended up resorting to.
@wilerson
You are correct, it should allow for pct encoded values. We just released an update that relaxed a number of these restrictions. If you have time, could you try with 10.2.3?
for request param: "filter[param]", works with 10.2.3
This should fixed via 10.2.3.
Hi @kdavisk6 ,
Request parameter starting with $ is still broken with 10.2.3 with spring-cloud-openfeign Greenwich.SR2
public interface Client{
@GetMapping("/api")
Result api(@RequestParam("$filter") String filter);
}
results in request /api/?$filter=%7B$filter%7D.
Changing to @RequestParam("%24filter") results in /api/?%24filter=%7B%24filter%7D
@fehmer
If you look at the previous comments, we did not make any changes to support $, but instead provided a workaround, which states that you must include the variable name as part of the uri template and not rely on automatic inclusion based on parameter naming. Here is the workaround again:
public interface Client {
@GetMapping("/api?$filter={filter}")
Result api(@RequestParam(filter) String filter);
}
This will result in the correct uri of /api?$filter=...
public interface Client {
@GetMapping("/api?$filter={filter}")
Result api(@RequestParam(filter) String filter);
}
I am afraid this workaround does not behave propertly. The resulting uri is in the format /api&$filter=AAA&filter=AAA which rejects in our case the API gateway because of one extra parameter. Is there any workaround without this extra parameter?
@mejmo2
Use @PathVariable instead. Also, please refrain from commenting on closed issues.
Most helpful comment
I might have missed something, but does RFC 6570 not allow for pct-encoded values? Because I still had this issue when using
"%24filter".Using a
Mapstill works, of course, so that's the workaround I ended up resorting to.