I am trying to convert the below kotlin code from RestTemplate to Feign client. The rest template code sends multiValueMap as request with content-type header multipart/form-data
and consumes JSON object as response.
RestTemplate Code:
var headers = HttpHeaders()
headers.contentType = MediaType.MULTIPART_FORM_DATA
headers.add("custom-header", "value")
val body: MultiValueMap<String, Any> = LinkedMultiValueMap()
body.add("field1", "value1")
body.add("field2", "value2")
val requestEntity = HttpEntity(body, headers)
return restTemplate.postForEntity("https://enmf7tx8y37x.x.pipedream.net/", requestEntity, Object::class.java)
In this case the request is sent as below:
Headers:
Host: enmf7tx8y37x.x.pipedream.net
X-Amzn-Trace-Id: Root=1-6303ecb2-19a833a044ab3bf83f74f256
Content-Length: 342
Accept: application/xml, text/xml, application/json, application/*+xml, application/*+json
Content-Type: multipart/form-data;boundary=_MtEGFIF4XK_aOU8QsXstQuCliV1-llj
custom-header: value
X-B3-TraceId: a67561ec329f9a16
X-B3-SpanId: a6cc94e403bfe318
X-B3-ParentSpanId: a67561ec329f9a16
X-B3-Sampled: 1
User-Agent: Apache-HttpClient/4.5.13 (Java/17.0.3)
Accept-Encoding: gzip,deflate
Body:
--_MtEGFIF4XK_aOU8QsXstQuCliV1-llj
Content-Disposition: form-data; name="field1"
Content-Type: text/plain;charset=UTF-8
Content-Length: 6
value1
--_MtEGFIF4XK_aOU8QsXstQuCliV1-llj
Content-Disposition: form-data; name="field2"
Content-Type: text/plain;charset=UTF-8
Content-Length: 6
value2
--_MtEGFIF4XK_aOU8QsXstQuCliV1-llj--
I tried to do the same in Feign client:
code:
/*val headers = HttpHeaders()
headers.contentType = MediaType.MULTIPART_FORM_DATA
headers.add("custom-header", "value")*/
val body: MultiValueMap<String, Any> = LinkedMultiValueMap()
body.add("field1", "value1")
body.add("field2", "value2")
val result = testClient.test("value", body)
Feign Client:
@FeignClient(
value = "testClient",
url = "https://enmf7tx8y37x.x.pipedream.net/"
)
interface TestClient {
@PostMapping(
consumes = [MediaType.MULTIPART_FORM_DATA_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
fun test(
@RequestHeader(value = "custom-header") customHeader: String,
@RequestPart("request") request: MultiValueMap<String, Any>
): ResponseEntity<Object>
}
The header are fine but no value present in the body.
Header:
Host: enmf7tx8y37x.x.pipedream.net
X-Amzn-Trace-Id: Root=1-6303ef0f-78c869881a5b27d0707eab9e
Content-Length: 17
Accept: application/json
Authorization: Basic aHlwb2xhYjp0ZXN0c211cmY=
Content-Type: multipart/form-data; charset=UTF-8; boundary=182c75dd399
custom-header: value
X-B3-TraceId: 2989eb4f12e3d417
X-B3-SpanId: 23414bcdf365784c
X-B3-ParentSpanId: 2989eb4f12e3d417
X-B3-Sampled: 1
User-Agent: Java/17.0.3
Body:
--182c75dd399--
I had to add consumes
value as multipart/form-data instead of json to get the right header values for Accept
and Content-Type
.
How can I populate the request using Feign client? If the @RequestPart
is String then the value is sent in the body but any other data type like multiValueMap, byteArray, etc were not working
In feign client you cannot use MultiValueMap directly. You have to use MultipartFile
datatype for bytearray and for the remaining metadata fields you need to mention each one as a separate argument in the method. Then FeignClient will generate the same request like the one you showed when using RestTemplate.
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.multipart.MultipartFile
@FeignClient(
value = "testClient",
url = "https://enmf7tx8y37x.x.pipedream.net/"
)
interface TestClient {
@PostMapping(
consumes = [MediaType.MULTIPART_FORM_DATA_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
fun test(
@RequestHeader(value = "custom-header") customHeader: String,
@RequestPart(name = "file") file: MultipartFile,
@RequestPart(name = "field1") field1: String
): ResponseEntity<Object>
}
code sample for How to create MultiPartFile:
import org.springframework.mock.web.MockMultipartFile
//val multipartFile: MultipartFile = MockMultipartFile("filename", byteArray)
val multipartFile: MultipartFile = MockMultipartFile("filename", "filename", "content type like application/pdf", byteArray)