Spring Boot OpenFeign: java .lang.IllegalStateException: метод имеет слишком много параметров тела - PullRequest
1 голос
/ 14 февраля 2020

Примеры ниже демонстрируют проблемы, возникающие при использовании OpenFeign. Проблема становится очевидной, когда ваш объект ответа имеет слишком много полей, что приводит к ошибке: Метод имеет слишком много параметров . Пример 1 работает отлично, но пример 2 не работает.

В примере 1 используется HTTP.POST с тем же объектом ответа, что и в примере 2, в котором используется HTTP.GET.

Почему OpenFeign ограничивает поля в методе HTTP.GET, и выбросить исключение? Я не могу использовать HTTP.POST, чтобы получить / извлечь / прочитать ресурс. BAD REST API Стандарты дизайна.

Используя один и тот же объект ответа, для HTTP.POST (который работает) происходит сбой HTTP.GET

public interface ClientFeignV2 {

//Example 1 
@Headers("Content-Type: application/json") @RequestLine("POST api/v2/clients") ClientResponse findAllClientsByUid1(@RequestBody ClientRequest request);

//Example 2
@Headers("Content-Type: application/json")
@RequestLine("GET api/v2/clients/{uid}")
ClientResponse findAllClientsByUid(@PathVariable(value = "uid") String uid,
                                         @RequestParam(value = "limit", required = false) Integer limit,
                                         @RequestParam(value = "offset", required = false) Integer offset);
}

StackTrace:

Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract com.services.requestresponse.ClientResponse com.microservice.gateway.feign.v2.ClientFeignV2.findAllClientsByUid(java.lang.String,java.lang.Integer,java.lang.Integer)
at feign.Util.checkState(Util.java:128) ~[feign-core-9.4.0.jar:na]
at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:114) ~[feign-core-9.4.0.jar:na]
at feign.Contract$BaseContract.parseAndValidatateMetadata(Contract.java:64) ~[feign-core-9.4.0.jar:na]
at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:146) ~[feign-core-9.4.0.jar:na]
at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:53) ~[feign-core-9.4.0.jar:na]
at feign.Feign$Builder.target(Feign.java:209) ~[feign-core-9.4.0.jar:na]
at feign.Feign$Builder.target(Feign.java:205) ~[feign-core-9.4.0.jar:na]
at com.microservice.gateway.service.v2.impl.ClientServiceV2Impl.<init>(ClientServiceV2Impl.java:27) ~[classes/:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_222]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_222]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_222]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_222]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:203) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
... 40 common frames omitted

Я прошел через Документация OpenFeign, и она поддерживает вышеуказанную реализацию. Если я не могу найти решение этой проблемы, мне придется прибегнуть к обходному пути и использовать HTTP.POST и @RequestBody, которые не являются идеальным решением в соответствии со стандартами проектирования Rest-API.

1 Ответ

1 голос
/ 18 февраля 2020

Согласно тегам, вы используете Spring Boot, очевидно, с Spring Cloud OpenFeign. И дело в том, что вы смешиваете два разных контракта Feign.

Аннотации типа @RequestLine и @Headers взяты из core feign library . Вы можете использовать его как декларативный HTTP-клиент, и не только в приложениях Spring (в этом случае не нужно использовать аннотации Spring).

Таким образом, правильный «пример 2» с простым Feign может быть например:

@Headers("Content-Type: application/json")
@RequestLine("GET api/v2/clients/{uid}?limit={limit}&offset={offset}")
ClientResponse findAllClientsByUid(@Param("uid") String uid,
                                   @Param("limit") Integer limit,
                                   @Param("offset") Integer offset);

С другой стороны, такие вещи, как @RequestParam и @PathVariable, принадлежат Spring Web. Их можно использовать, если у вас есть библиотека Spring Cloud OpenFeign (кстати, основной компонент - один из ее компонентов). Эта библиотека поддерживает SpringMvcContract , что в ее случае позволяет использовать обычные аннотации Spring Web для определения сопоставления запросов вместо спецификаций Feign * c.

В случае SpringMvcContract, «пример 2» может выглядеть следующим образом:

@GetMapping(value = "api/v2/clients/{uid}", consumes = MediaType.APPLICATION_JSON_VALUE)
ClientResponse findAllClientsByUid(@PathVariable(value = "uid") String uid,
                @RequestParam(value = "limit", required = false) Integer limit,
                @RequestParam(value = "offset", required = false) Integer offset);

Стоит отметить, что в Spring Cloud OpenFeign по умолчанию используется второй подход . Чтобы вернуть его в обычный контракт Feign, определите пользовательский компонент Contract ( source ):

@Bean
public Contract feignContract() {
    return new feign.Contract.Default();
}
...