如何使用 Spring Cloud Feign 发布表单 URL 编码的数据

Posted

技术标签:

【中文标题】如何使用 Spring Cloud Feign 发布表单 URL 编码的数据【英文标题】:How to POST form-url-encoded data with Spring Cloud Feign 【发布时间】:2016-06-18 14:29:42 【问题描述】:

使用spring-mvc注解:

如何定义一个@FeignClient 可以POST form-url-encoded

【问题讨论】:

【参考方案1】:

对 Feign 使用 FormEncoder

https://github.com/OpenFeign/feign-form

你的 Feign 配置可能如下所示:

class CoreFeignConfiguration 
  @Autowired
  private ObjectFactory<HttpMessageConverters> messageConverters

  @Bean
  @Primary
  @Scope(SCOPE_PROTOTYPE)
  Encoder feignFormEncoder() 
      new FormEncoder(new SpringEncoder(this.messageConverters))
  

然后,客户端可以这样映射:

@FeignClient(name = 'client', url = 'localhost:9080', path ='/rest',
    configuration = CoreFeignConfiguration)
interface CoreClient 
    @RequestMapping(value = '/business', method = POST, 
                 consumes = MediaType.APPLICATION_FORM_URLENCODED)
    @Headers('Content-Type: application/x-www-form-urlencoded')
    void activate(Map<String, ?> formParams)

【讨论】:

注意这一行Map&lt;String, ?&gt; formParams,问号是必填项。 对于那些不认识 groovy 的人 - 这是在 groovy 中,因此不是“return”,“;”等:) 不是很有用,它要求我的 pojo 有 @FormProperty 但不检查超类,我不能在客户端调用中单独提供 20 个表单属性。 你不需要额外的 headers 注释,feign 自动将其放置在消耗配置中。【参考方案2】:

带有简化版 kazuar 解决方案的完整 Java 代码,可与 Spring Boot 配合使用:

import java.util.Map;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;

@FeignClient(name = "srv", url = "http://s.com")
public interface Client 

    @PostMapping(value = "/form", consumes = APPLICATION_FORM_URLENCODED_VALUE)
    void login(@RequestBody Map<String, ?> form);

    class Configuration 
        @Bean
        Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) 
            return new SpringFormEncoder(new SpringEncoder(converters));
        
    

依赖:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

【讨论】:

可能无关但值得一提。如果您期待 JSON 响应。您可能需要为Decoder 配置@Bean return new GsonDecoder(); 不,spring boot中已经配置了Jackson解码器。 是的,没错。默认的 JSONDecoder 已经配置好了。但是,我已启用 gson 作为默认转换器并使用自定义版本 @Bean Gson upbeatGson() return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create(); - 因此我提到了。否则,如果 JSONDecoder - 默认版本有效,则不需要。 在您的 @FeignClient 注释中只是错过了 configuration = yourClass.Configuration.class 正如@HenriqueSchmitt 所说,我必须设置@FeignClient(configuration = Client.Configuration.class) 才能使其工作。答案应该被编辑【参考方案3】:

对于 POST 中的 url-form-encoded 数据,您必须在 Feign 编码器中使用 FormEncoder

将依赖项添加到您的应用中:

马文:

<dependency>
  <groupId>io.github.openfeign.form</groupId>
  <artifactId>feign-form</artifactId>
  <version>3.8.0</version>
</dependency>

像这样将 FormEncoder 添加到 Feign.Builder 中:

SomeFeign sample  = Feign.builder()
                      .encoder(new FormEncoder(new JacksonEncoder()))
                      .target(SomeFeign.class, "http://sample.test.org");

在 Feign 界面中

@RequestLine("POST /submit/form")
@Headers("Content-Type: application/x-www-form-urlencoded")
void from (@Param("field1") String field1, @Param("field2") String field2);

更多信息请参考: https://github.com/OpenFeign/feign-form

【讨论】:

此方法会导致“Java.lang.NoSuchMethodError”。【参考方案4】:

为了补充accepted answer,也可以使用POJO代替Map&lt;String, ?&gt;来传递表单参数给feign客户端:

@FeignClient(configuration = CustomConfig.class)
interface Client 

    @PostMapping(
        path = "/some/path", 
        consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    void postComment(CommentFormDto formDto);
    ...

...

class CustomConfig 
    @Bean
    Encoder formEncoder() 
        return new feign.form.FormEncoder();
    

...

class CommentFormDto 

    private static String willNotBeSerialized;

    private final Integer alsoWillNotBeSerialized;

    @feign.form.FormProperty("author_id")
    private Long authorId;

    private String message;

    @feign.form.FormProperty("ids[]")
    private List<Long> ids;
    
    /* getters and setters omitted for brevity */  

这将导致请求的正文如下所示:

author_id=42&message=somemessage&ids[]=1&ids[]=2

@FeignProperty注解允许设置自定义字段名称;请注意 POJO 的 static 或 final 字段以及继承的字段,不会被序列化为表单内容。

【讨论】:

【参考方案5】:

对于 Feign.Builder,我的工作没有 JacksonEncoder,只有 Feign FormEncoder:

将 FormEncoder 添加到您的 Feign.Builder:

SomeFeign sample  = Feign.builder()
                  .encoder(new FormEncoder())     <==difference here
                  .target(SomeFeign.class, "http://sample.test.org");

我在 pom.xml 中添加的 feign 依赖:

<dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-core</artifactId>
        <version>11.8</version>
    </dependency>

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-jackson</artifactId>
        <version>11.8</version>
    </dependency>
    
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form</artifactId>
        <version>3.8.0</version>
    </dependency>

pom.xml 中的父级是:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.2</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

Ramanan 给出的 feign 界面:

@RequestLine("POST /submit/form")
@Headers("Content-Type: application/x-www-form-urlencoded")
void from (@Param("field1") String field1, @Param("field2") String field2);

【讨论】:

以上是关于如何使用 Spring Cloud Feign 发布表单 URL 编码的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Cloud Feign 发布表单 URL 编码的数据

spring cloud中如何通过feign调整负载均衡规则

08 在Spring Cloud中使用Feign

Spring Cloud:如何在没有 Ribbon 的情况下使用 Feign

Spring-Cloud之Feign

将 Zuul、Hystrix(和 Feign)与 Spring Cloud HATEOAS 一起使用时如何转发标头?