Closed sparqueur closed 5 years ago
Here is a working implementation (ReflectiveFeign only) based on feign core [9.5.1] feign.zip Hope you will try to integrate this to a futur release
I managed to make @ModelAttribute work with it (someone might need it) : spring-feign-model-attribute.zip
Support for expanding objects into Query Parameters was added in Feign 9.7. Take a look at the Dynamic Query Parameters documentation.
As for additional ways to process an interface, that can be done by defining your own Contract
. As you've already discovered, this is how Spring OpenFeign works.
If you need to process the request in a different way, you can use a RequestInterceptor
to refine the request before sending to the client, including adding/removing/changing headers, decorating the request with more information, and so on. You can use an Encoder
to control how your request body is represented to the endpoint. You can use a Decoder
to control how to process the results. If you need to manage the low level execution of the request, you can build your own Client
. All of these capabilities provide a working model for managing each part of the request lifecycle and are exposed as part of Feign's public interface.
This is the preferred way to extend Feign. HACKING describes the reason behind this philosophy.
Getting back to your original question, I recommend using a RequestInterceptor
to handle any special pre-processing requirements and a custom Encoder
to do more robust Request Body handling.
As for the @ModelAttribute
work, that is better suited for the Spring Cloud OpenFeign project.
Thanks for your answer. Sorry, I might be wrong but RequestInterceptor is not a solution as you dont have access to the function call parameters. The encoder is neither usable as it is for body parameter only (which is not my case). The feature i ask is just missing. But maybe I am wrong with something ? Thanks in advance Regards
If you have a particular use case in mind that could help clarify. Can you provide one?
I'm working with Jhipster (https://www.jhipster.tech/). There is a nice Filter mechanism to request for resources.
Here is an example.
Class StringFilter {
String contains;
String startsWith;
String equals;
}
Then I have a global filter object :
Class UserRequestFilter {
StringFilter firstname;
StringFilter lastname;
StringFilter city;
}
My java method corresponding to the REST end points is :
public List<User> find(@ModelAttribute UserRequestFilter filters);
My "filters" object is decomposed in request params according to attribute names
This let me do such kind of request :
GET /user?firstname.contains=antoine&city.equals=lyon
I find this very nice.
I would like to be able to implement a feign client to call my endpoint and use this UserRequestFilter and have a generic object to request param encoder.
Currently, I can implement a custom encoder but it only let me transform an input object to ONE string.
=> I can for instance use a JSON serialization and obtain such kind of request :
GET /user?filters={firstname:{contains:antoine},city:{equals:lyon}}
but I realy prefer the following request :
GET /user?firstname.contains=antoine&city.equals=lyon
Hope this example is enough understandable.
Thank's in advance
May I suggest that you look at the documentation for Dynamic Query Parameters again. You can provide a QueryMapEncoder
that will allow you to process the QueryMap
annotated object and generate the specific Query String you need. This is different from the Param.Encoder
in that it can process an entire object and does not generate a string, but the Query String Map.
class FilterEncoder implements QueryMapEncoder {
@Override
public Map<String, Object> encode(Object object) throws EncodeException {
// process your object, creating a map where the key is the query parameter name
// and the value is the query parameter value
}
}
To achieve what you are looking for, you will need to build this map accordingly. One possible approach would be to inspect the fields on the object, and then delegating to another component whose responsibility is to encode *Filter
objects.
class StringFilterEncoder {
SimpleImmutableEntry<String, String> encode(StringFilter filter) {
// return a key, value pair from the string filter
}
}
class FilterQueryMapEncoder implements QueryMapEncoder {
public Map<String, Object> encode(Object object) {
Map<String, Object> queryMap = new LinkedHashMap<>();
Class<?> type = object.getClass();
for (Field field : type.getDeclaredFields()) {
// check to see if it is a StringFilter and delegate
queryMap.putIfAbsent(StringFilterEncoder.encode(...));
}
return queryMap;
}
}
Your Interface then can look like this
@RequestLine("GET /users")
interface Users {
public List<User> get(@QueryMap UserRequestFilter filter);
}
Users api = Feign.builder()
.queryMapEncoder(FilterQueryMapEncoder())
.target(User.class, "http://endpoint");
I'm going to close this issue and recommend that questions like this be made on Stack Overflow, allowing more users to help you.
In my use case I wish I could use custom parameter processor.
Currently, there is no way to make feign call an external processor which could deaply process a parameter. For instance, add multiple query params according to the parameter or headers etc...
The only thing we have are Expander for request params, but it only let us serialize a parameter to a String.
Please provide us a "
@CustomProcessor(MyCustomProcessor.class)
" annotation in order to manually do our stuff directly with the restTemplate :Add a map in MethodMetada, you could then call it the ReflectiveFeign.create function. Voila, easy to say, hard to do :-)
My use case :
I'm using spring-cloud-feign and share a contract between the server and client. One spring mvc annotation is @ModelAttribute => Object are serialized in multiple request params representing the attributes.
Eg : UserRequest :
public List<User> searchUser (@ModelAttribute UserRequest criteria)
==> GET /search?name=toto&age=12With CustomProcessors I could handle this !
Thank's in advance