feign icon indicating copy to clipboard operation
feign copied to clipboard

Support Conditional Parameter Expansion

Open bsideup opened this issue 8 years ago • 19 comments

Hi!

We define our Spring MVC APIs with @RequestParam Optional<Type> something.

It looks like Feign doesn't handle it well and uses "toString()" version so that Spring fails to convert String -> Optional

bsideup avatar Jan 19 '17 17:01 bsideup

see #294

spencergibb avatar Jan 19 '17 18:01 spencergibb

hey @spencergibb, I'm afraid this is not the same, #294 was about the decoder, and it's not hard to use a custom decoder to make it work, but, in case of the request parameters, I don't know any good way to make it work :(

bsideup avatar Jan 19 '17 19:01 bsideup

Yeah, I specifically didn't say it was a duplicate, just regarding Optional.

spencergibb avatar Jan 19 '17 20:01 spencergibb

right now, the work around is param parser

codefromthecrypt avatar Jan 20 '17 00:01 codefromthecrypt

@adriancole sounds exactly what I want. Where can I find it? Thanks!

bsideup avatar Jan 20 '17 09:01 bsideup

oh.. you are asking about spring mvc annotations. sorry I was answering about feign.Param.Expander

codefromthecrypt avatar Jan 20 '17 09:01 codefromthecrypt

if one were hacking things.. they could change the contract so that it populates MethodMetadata.indexToExpanderClass (this is what the default contract does, handling Param.Expander.

codefromthecrypt avatar Jan 20 '17 09:01 codefromthecrypt

Same here, optional parameter is very useful if you want to reset optional parameter, otherwise you just can't do that.

It's already supported by Spring MVC:

@GetMapping(path = "/test")
    public void test(
        @RequestParam(value = "name", required = false) Optional<String> name
    ) {
        System.out.println("is present " + name.isPresent());
    }

Request set name http://localhost:8132/test?name=hello will have true. Reset name request http://localhost:8132/test?name will have true. No name reset request http://localhost:8132/test? will have false.

Hronom avatar Jul 18 '18 20:07 Hronom

Well, feign core now is on java 8, so, this should be doable

velo avatar Jul 20 '18 06:07 velo

I think I was missing something, it seems that contract in Spring MVC is next: Request set name http://localhost:8132/test?name=hello will have hello. Reset name request http://localhost:8132/test?name will have `` - empty String. No name reset request http://localhost:8132/test? will have null - null String.

Seems like optional adds nothing in my case.

For my case I think I need custom class that will be able to show when parameter actually is in request. Like ResponseEntity for returned body.

Example:

   @GetMapping(path = "/test")
    public void test(
        @RequestParam(value = "name", required = false) RequestParamEntity<String> name
    ) {
        System.out.println("is present " + name.isPresent());
        System.out.println("has value " + name.hasValue());
    }

For request http://localhost:8132/test?name=hello output will be:

is present true
has value true

For request http://localhost:8132/test?name= (questionable) output will be:

is present true
has value true

For request http://localhost:8132/test?name output will be:

is present true
has value false

For request http://localhost:8132/test? output will be:

is present false
has value false

But this also should be implemented in Spring MVC.

Hronom avatar Jul 20 '18 08:07 Hronom

We can support Optional<T> as a return type. but we do not support is as a @Param. I'll add this to the backlog for us to consider, however; feel free to submit a PR for this if you cannot wait.

kdavisk6 avatar Jul 20 '18 16:07 kdavisk6

I would like to help add Optional<T> support in @Param, just need some direction on where to start looking

RicardoRdzG avatar Aug 22 '18 16:08 RicardoRdzG

I think that we will first need to agree on a specification. These should focus on the result of Optional.isPresent(). We would also need to limit the scope to Feign's default contract and annotation, @Param, which does not support a required attribute. Given these restrictions, I see the following use cases for us to define:

  • isPresent is true
  • isPresent is false
  • Parameter is not provided or null

Thoughts?

kdavisk6 avatar Sep 24 '18 14:09 kdavisk6

@kdavisk6 I think Optional will adds nothing from what actually needed, check my previous post for suggested custom class RequestParamEntity(name is questionable) and suggested behaviour of it.

That behaviour you can't implement using Optional.

Hronom avatar Sep 24 '18 14:09 Hronom

Apologies. I will update the title of this issue to avoid any further confusion.

Reading through it again, the behavior you are looking for is to have more control over what the resulting expanded request parameter name/value pair is. A recent update, not yet released as part of #778, standardizes how Feign handles Query String, Headers and Body template parameter expansion. The logic now adheres to RFC 6570 - URI Templates Level 1 Expansion. Resolved Query String variables result in a name=value expansion and unresolved variables result in the name/value pair being removed from the resulting uri.

The part of your request that is challenging is the conditional logic applied if the parameter cannot be resolved. This feels a lot like what was discussed in #726. One possible approach would be to extend the Expander interface that can be used on a @Param, allowing for expansion of the entire pair and not just the value, but that would be a breaking change.

kdavisk6 avatar Sep 24 '18 15:09 kdavisk6

Hey,

what's the status of this enhancement?

yami12376 avatar Sep 24 '19 13:09 yami12376

@yami12376

We are considering adding support for Level 2-4 uri template expression which will address most of what is discussed in this request. Specifically, we will adhere to the RFC rules regarding what to do when the value is undefined, which in our case is an explicit null. Empty Strings are not considered undefined, just empty, which are still included.

For example, using form style expressions http://localhost:8132/test{?name}, the result would be as follows:

name is present
http://localhost:8132/test?name=value
name is present and empty
http://localhost:8132/test?name=
name is not present
http://localhost:8132/test

The only situation this will not handle is the case where a user may want to override this behavior for a single expression. Using the example above where the desired result is the variable on the query string without the = sign, we would need provide a facility to override the expansion logic. We may also consider extending the capability of the Expander concept for this; however for Feign 11, we are going to focus primarily on the template levels.

kdavisk6 avatar Dec 27 '19 15:12 kdavisk6

We can support Optional<T> as a return type. but we do not support is as a @Param. I'll add this to the backlog for us to consider, however; feel free to submit a PR for this if you cannot wait.

Has support to Optional<T> @Param been implemented in Feign?

dbaltor avatar Jul 07 '20 15:07 dbaltor

this has just bitten me in 2023. i was expecting optional empty to supply no parameter, but nope gives ?param=

michaelnewsuk avatar Oct 18 '23 09:10 michaelnewsuk