question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

OkHttp and Feign produce "method GET must not have a request body."

See original GitHub issue

Imagine that we have searchable api which accepts “request” and “pageable” like /api/1.0/search?sort=id,desc&size=20000&name=feign&version=10.3.0 In that case I can’t use “@QueryMap”, because it should present only on one parameter.

This is our REST API interface:

@Headers(value = ["Accept: application/json", "Content-Type: application/json"])
interface ControllerApi {

  @RequestLine("GET /api/1.0/search?{request}&{pageable}")
  fun someEndpoint(
      @Param(
          value = "request",
          expander = GenericDataClassExpander::class
      ) request: SearchRequestDto,
      @Param(
          value = "pageable",
          expander = PageableExpander::class) pageable: Pageable
  )

This is our custom DTO definition:

data class SearchRequestDto(
    @field:ApiModelProperty("search by name contains")
    @field:JsonProperty("name")
    var name: String? = null,

    @field:ApiModelProperty("search by version contains")
    @field:JsonProperty("version")
    var version: String? = null
)

Pageable is Spring commonly used interface.

These are the expanders that we are using to generate the final request:

class PageableExpander : Param.Expander {
  override fun expand(value: Any): String {
    val pageable = value as? Pageable
        ?: throw IllegalStateException("Error while expanding Pageable")
    val page = pageable.pageNumber
    val size = pageable.pageSize
    val sort = pageable.sort
    var res = ""
    res += "page=$page&"
    res += "size=$size&"
    if (sort != null) {
      res += "sort=$sort"
    }

    return res
  }
}

class GenericDataClassExpander : Param.Expander {
  override fun expand(value: Any): String {
    val res = value::class.declaredMemberProperties.fold("") { acc, field ->
      field as KProperty1<Any, *>
      val fieldValue = field.get(value)
      if (fieldValue != null) {
        acc + "${field.name}=$fieldValue" + "&"
      } else {
        acc
      }
    }
    return res.removeSuffix("&")
  }
}

The problem that we faced is that while using this API, Feign is unable to construct a proper QueryTemplate, since the CollectionFormat is supposed to be something like ?[key]=[value], e.g. key1={value1}&key2={value2}. However, we would like to omit having keys and just include already expanded values like ?{expanded-value}.

Therefore, if we do not follow the standart CollectionFormat ?[key]=[value], the QueryTemplate gets constructed with empty templateChunks, so the getVariables method returns empty array. Then, in feign.Contract we could see this:

if (!data.template().hasRequestVariable(name)) {
     data.formParams().add(name);
}

In our case when getVariables is empty, it will add our query parameters to form parameters and therefore to the body of RequestTemplate, which is not what we want. When trying to run this request via the OkHttpClient, we will receive an exception that “method GET must not have a request body.”

This behavior seems strange to me, since it basically violates the standards. And this is obviously not the behavior what I want when I generate the custom request line myself with expanders.

Maybe you could consider adding a simple condition to prevent adding any form params for the GET request like this?

HttpMethod method = HttpMethod.valueOf(data.template().getMethod());
if (method != HttpMethod.GET || !data.template().hasRequestVariable(name)) {
     data.formParams().add(name);
}

I would love to hear any response of this, but please be aware that I cannot change the API that I am using and I have to stick with it.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:11
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
scrat98commented, Aug 14, 2019

@kdavisk6

Hi Kevin,

Yes, I checked the @QueryMap as I wrote in the begging. And I also wrote, “that I cannot change the API” because “feign api” it’s an interface first of all and I would like to use it for Inheritance and for consistency between web-controller and api to this controller.

So basically I have

class Controller : ConrollerApi {
  override fun someEndpoint(request: SearchRequestDto, pageable: Pageable) {
  }
}

And I would not prefer either use “inconsistency way” where is my controller and api to that controller not inherited like this

class Controller {
  override fun someEndpoint(request: SearchRequestDto, pageable: Pageable) {
  }
}

@Headers(value = ["Accept: application/json", "Content-Type: application/json"])
interface ControllerApi {
  @RequestLine("GET /api/1.0/search?{parameters}")
  fun someEndpoint(@QueryMap parameters: Map<String, String>)
}

or “bad arguments for controller way” like this:

class Controller : ConrollerApi {
  override fun someEndpoint(parameters: Map<String, String>) {
  }
}

But I still can’t understand why request parameters with expanders should be in forms parameters(in body of request when it’s request parameters)

0reactions
kdavisk6commented, Jan 18, 2020

I’ve considered a few other options but I think the best answer for this issue has been merged and released in #1144. With regards to the naming issue, I think that #1019 will provide the right balance of support and flexibility. I’m going to close this issue and ask that any additional comments be placed in #1019

Read more comments on GitHub >

github_iconTop Results From Across the Web

GET request with request body in OkHttp - java - Stack Overflow
Basically, you can create POST method with a builder and set its method field to "GET" later using the reflection. This will do...
Read more >
feign method GET must not have a request body. - CSDN博客
method GET must not have a request body. Post 请求好好的,Get咋报错呢,哈哈. debug看这个方法feign.okhttp.
Read more >
7. Declarative REST Client: Feign - Spring Cloud
Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it....
Read more >
Decode an OkHttp JSON Response - Baeldung
Learn several techniques for decoding a JSON OkHttp response. ... For this, we can access the body via the body() method of the...
Read more >
RequestTemplate (Feign Core 10.5.1 API) - Javadoc.io
Create a Request Template from an existing Request Template. ... Due to this, use of this method is not recommended and remains for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found