Using spring-mvc annotations:
- How can I define an
@FeignClientthat canPOSTform-url-encoded?
Using spring-mvc annotations:
@FeignClient that can POST form-url-encoded?Full Java code with a simplified version of kazuar solution, works with 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:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Just to complement accepted answer, one can also use POJO instead of Map<String, ?> in order to pass form parameters to feign client:
@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 */
}
That will result in request with body that looks something like this:
author_id=42&message=somemessage&ids[]=1&ids[]=2
@FormProperty annotation allows to set custom field names; please note that static or final fields of POJO, along with inherited ones, will not be serialized as form content.
You must use FormEncoder in Feign encoder for url-form-encoded data in POST.
Include the dependency to your app:
Maven:
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
Add FormEncoder to your Feign.Builder like so:
SomeFeign sample = Feign.builder()
.encoder(new FormEncoder(new JacksonEncoder()))
.target(SomeFeign.class, "http://sample.test.org");
In the Feign interface
@RequestLine("POST /submit/form")
@Headers("Content-Type: application/x-www-form-urlencoded")
void from (@Param("field1") String field1, @Param("field2") String field2);
Ref for more info: https://github.com/OpenFeign/feign-form
For the Feign.Builder, mine worked without the JacksonEncoder, just the Feign FormEncoder:
Add FormEncoder to your Feign.Builder:
SomeFeign sample = Feign.builder()
.encoder(new FormEncoder()) <==difference here
.target(SomeFeign.class, "http://sample.test.org");
The feign dependencies I added to pom.xml:
<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>
Parent in pom.xml is:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
The feign interface as Ramanan gave:
@RequestLine("POST /submit/form")
@Headers("Content-Type: application/x-www-form-urlencoded")
void from (@Param("field1") String field1, @Param("field2") String field2);
Tested in Kotlin: What worked for me was the following:
@Configuration
class FeignFormConfiguration {
@Bean
fun multipartFormEncoder(): Encoder {
return SpringFormEncoder(SpringEncoder {
HttpMessageConverters(
RestTemplate().messageConverters
)
})
}
}
@FeignClient(
value = "client",
url = "localhost:9091",
configuration = [FeignFormConfiguration::class]
)
interface CoreClient {
@RequestMapping(
method = [RequestMethod.POST],
value = ["/"],
consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE],
produces = ["application/json"]
)
fun callback(@RequestBody form: Map<String, *>): AnyDTO?
}
// ----- other code --------
@Autowired
private lateinit var coreClient: CoreClient
fun methodName() {
coreClient.callback(form)
}
// ----- other code --------