New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest supporting POJO as request parameter #1253

Open
adriancole opened this Issue Aug 12, 2016 · 11 comments

Comments

Projects
None yet
8 participants
@adriancole
Contributor

adriancole commented Aug 12, 2016

From @wanghongfei on August 12, 2016 4:14

I'm using Feign with Spring Cloud Netflix stack. In SpringMVC we can use POJO as request parameter which is very convenient:

@RequestMapping(value = "/group/list")
    public List<AdvertGroupVO> list(AdvertGroupVO vo) throws AlanException {

        return adGroupService.list(vo);
    }

But in Feign I have to write lots of @RequestParam:

@FeignClient(name = "fooService")
public interface foo {

    @RequestMapping(value = "/group/list")
    public List<AdvertGroupVO> list(@RequestParam Integer id,
                                      @RequestParam String name,
                                      @RequestParam ... ...);
}

I think it good for feign's user. Thanks!

Copied from original issue: OpenFeign/feign#438

@adriancole

This comment has been minimized.

Contributor

adriancole commented Aug 12, 2016

In feign's DefaultContract, a parameter without any annotations is assumed to be processed by feign's Encoder, which can do what's been requested here. We can decide if this behavior makes sense or not.

@wanghongfei

This comment has been minimized.

wanghongfei commented Aug 12, 2016

Thanks for @adriancole 's reply. In current Spring Cloud Netflix Feign's implementation it seems feign doesn't encode POJO to HTTP request parameter by default, or there's something I do not know yet

@adriancole

This comment has been minimized.

Contributor

adriancole commented Aug 12, 2016

@wanghongfei are you saying that you want something to literally write each field as a query parameter (ex via reflection)? If so, that would be a custom encoder and I don't think it is a high-reuse thing at this point.

@adriancole

This comment has been minimized.

Contributor

adriancole commented Aug 12, 2016

closest thing in feign upstream is @QueryMap which explodes a map into query parameters. I'm not sure if that is supported in MVC @RequestParam

@wanghongfei

This comment has been minimized.

wanghongfei commented Aug 12, 2016

@adriancole Thanks, I'll try @QueryMapor define my own encoder.

@wongloong

This comment has been minimized.

wongloong commented Dec 8, 2016

@wanghongfei 请问这个问题你最后怎么解决的?

@asarkar

This comment has been minimized.

Contributor

asarkar commented Jun 25, 2017

@adriancole @wanghongfei My PR #1361 allows @RequestParam to be used without a value/name and with a Map. Not with a POJO, but that's the closest thing to POJO.

@barrer

This comment has been minimized.

barrer commented Aug 18, 2017

@wongloong
supporting POJO as request parameter
solution: http://www.itmuch.com/spring-cloud-sum/feign-multiple-params/

Let's talk about how to use Feign to construct a POST request with multiple parameters. Assume that the Service Provider's Controller is written as follows:
@RestController
public class UserController {
  @PostMapping("/post")
  public User post(@RequestBody User user) {
    ...
  }
}
How do we use Feign to request for it? The answer is very simple, example:
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/post", method = RequestMethod.POST)
  public User post(@RequestBody User user);
}

tips: method = RequestMethod.POST and @RequestBody
I tested successfully!

@spencergibb

This comment has been minimized.

Member

spencergibb commented Aug 18, 2017

Right, this issue is for a GET method, not post.

@SeauWong

This comment has been minimized.

SeauWong commented Aug 18, 2017

yml:

feign:
  httpclient:
    enabled: true

MAVEN:

            <!--httpClient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.3</version>
            </dependency>

            <dependency>
                <groupId>com.netflix.feign</groupId>
                <artifactId>feign-httpclient</artifactId>
                <version>8.17.0</version>
            </dependency>

SendPoint(get method):

@RequestMapping(value = "/user/list",method = RequestMethod.GET,consumes = "application/json")
    List<User> users(@RequestBody QueryBean queryBean);

ReceivePoint(post method):

@RequestMapping(value = "/user/list",method = RequestMethod.POST,consumes = "application/json")
    List<User> users(@RequestBody QueryBean queryBean){
                      func();
}
@charlesvhe

This comment has been minimized.

charlesvhe commented Mar 7, 2018

I use RequestInterceptor to solve this problem:

public class YryzRequestInterceptor implements RequestInterceptor {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void apply(RequestTemplate template) {
        // feign 不支持 GET 方法传 POJO, json body转query
        if (template.method().equals("GET") && template.body() != null) {
            try {
                JsonNode jsonNode = objectMapper.readTree(template.body());
                template.body(null);

                Map<String, Collection<String>> queries = new HashMap<>();
                buildQuery(jsonNode, "", queries);
                template.queries(queries);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
        if (!jsonNode.isContainerNode()) {   // 叶子节点
            if (jsonNode.isNull()) {
                return;
            }
            Collection<String> values = queries.get(path);
            if (null == values) {
                values = new ArrayList<>();
                queries.put(path, values);
            }
            values.add(jsonNode.asText());
            return;
        }
        if (jsonNode.isArray()) {   // 数组节点
            Iterator<JsonNode> it = jsonNode.elements();
            while (it.hasNext()) {
                buildQuery(it.next(), path, queries);
            }
        } else {
            Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> entry = it.next();
                if (StringUtils.hasText(path)) {
                    buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
                } else {  // 根节点
                    buildQuery(entry.getValue(), entry.getKey(), queries);
                }
            }
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment