Send MultiValueMap as MultiPartFormData in Feign Client - spring-cloud-feign

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)

Related

C# reading OData $batch multipart/mixed response to a meaningful object(s)

I am consuming an OData Service, I am successfully POSTing my request (using RestSharp) to /$batch endpoint and getting the response. the response header contains
"Content-Type" : "multipart/mixed; boundary=<GUID>"
Body is
--C4254E82B51CFE5BD04201606B9AB7C50
Content-Type: multipart/mixed; boundary=C4254E82B51CFE5BD04201606B9AB7C51
Content-Length: 2221
--C4254E82B51CFE5BD04201606B9AB7C51
Content-Type: application/http; charset=utf-8
Content-Length: 2037
content-transfer-encoding: binary
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 1732
location: https://test.api/Event/CarEntries('4003581738')
dataserviceversion: 2.0
etag: W/"datetimeoffset'2021-04-21T00%3A49%3A45Z'"
{ JSON }
--C4254E82B51CFE5BD04201606B9AB7C51--
--C4254E82B51CFE5BD04201606B9AB7C50
Content-Type: application/http; charset=utf-8
Content-Length: 15116
content-transfer-encoding: binary
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 15017
dataserviceversion: 2.0
{ JSON }
--C4254E82B51CFE5BD04201606B9AB7C50--
How do I deserialise and extract the JSON Objects in my C# code? I do not want to invent a Regex pattern (well that is my last resort)
I did try using "Simple.OData.Client" (also a few others) but my request is not 100% compatible with the "Simple.OData.Client".
Also tried extracting using the below code but not necessary give me what I want
var sc = new StringContent(response.Content);
var content = sc.ReadAsStreamAsync().Result;
var streamContent = new StreamContent(content);
streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(response.ContentType);
var provider = streamContent.ReadAsMultipartAsync().Result;
Can someone giveme the best way to extract the Json objects ?
Thanks
Nero
I manage to get this working HttpClient and System.Net.Http.Formatting.Extension
Below is the code
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://service-url/Entity/v1.3/$batch"))
{
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Client-Id", "XXXXXXXXX");
// Add all the headers here
// this is your custom batch request
request.Content = new StringContent("--batch\ncontent-type: multipart/mixed;boundary=changeset\n\n--changeset\ncontent-type: application/http\nContent-Transfer-Encoding: binary\n\nPOST CarEntries HTTP/1.1\ncontent-type: application/json;charset=utf-8\naccept: application/json;\n\n{\n\"RefId\": \"Test\",\n\"Child\": {\n\"ChildId\": \"412000415\"\n}\n}\n--changeset--\n--batch--");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/mixed;boundary=batch"); // This is imporatnt - but please refer to your api documentation
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var multiPartContent = await response.Content.ReadAsMultipartAsync(); // This is part of the extension
var mixedContent = multiPartContent.Contents.First(); // you will have multiple contents, select the content you want
var data = await mixedContent.ReadAsStringAsync();// read it as string
Regex rg = new Regex(#"\{(.|\s)*\}"); // find the json object from mixed content
var json = rg.Match(data);
return JsonConvert.DeserializeObject<TMessage>(json.Value);
}
}
Hope this helps someone in the future. But still, my goal is to use "Simple.OData.Client" or "Microsoft.OData.Client"

How to view content delivered from HttpResponseMessage (ASP.NET MVC Web Api)

I create json object and assign it to a StringContent of my HttpResponseMessage instance. Everything works fine when I call the Web API action, the result is 200, the content-length is how it should be, but how to find the content itself, where is the json? What I get in the browser and in Postman is this:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
Content-Type: application/json
}
Why is this instead of my json string?
Content: System.Net.Http.StringContent
If what you are trying to achieve is to return a valid JSON response, then this is the way to go in Asp.Net MVC
public ActionResult HttpResponseMessage()
{
var oJSON = new { url = "path_to_file", hash = "aaaaaaaaaaaaaaaaa" };
return Json(oJSON, JsonRequestBehavior.AllowGet);
}
Response headers as seen by Postman:
Cache-Control →private
Content-Length →49
Content-Type →application/json; charset=utf-8
Date →Fri, 26 Oct 2018 13:31:44 GMT
Server →Microsoft-IIS/10.0
X-AspNet-Version →4.0.30319
X-AspNetMvc-Version →5.2
X-Powered-By →ASP.NET
X-SourceFiles →=?UTF-8?B?RTpcRXhhbSA3MCA0ODdcNzA0ODdcTVZDUm91dGVzXEhvbWVcSHR0cFJlc3BvbnNlTWVzc2FnZQ==?=
Response body as seen by Postman
{"url":"path_to_file","hash":"aaaaaaaaaaaaaaaaa"}

Beanshell code to perform HTTP post request instead of http sampler

I'm writing Beanshell code to perform HTTP post request instead of using HTTP sampler
My Code:
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.entity.StringEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
//Previous Response time
int responseTime = Integer.parseInt(String.valueOf(prev.getTime()));
//Previous Response Size
int size = Integer.parseInt(String.valueOf(prev.getResponseData().length));
//log.info("Reponse time " +responseTime);
//Previous Response Status Code
int responseCode = Integer.parseInt(String.valueOf(prev.getResponseCode()));
String testId =String.valueOf(${__time(yyyyMMdd)});
String executionTimestamp =String.valueOf(${__time(yyyyMMdd)});
//double int executionTimestamp = Integer.parseInt(${__time()});
String Transaction="Transaction_Login";
String applicationName ="Login";
String conversationId ="Sampledata";
String Status="";
String msg="mesages";
//Set Status according to responseCode
if(responseCode=="200"){
Status="OK";
}else {
Status="Fail";
}
HttpClient httpClient = HttpClientBuilder.create().build();
try{
StringEntity params =new StringEntity("\"testId\":\""+testId+"\",\"TransactionName\":\""+Transaction+"\",\"applicationName\":\""+applicationName+"\",\"conversationId\":\""+conversationId+"\",\"size\":\""+size+"\",\"status\":\""+Status+"\",\"messages\":\""+msg+"\",\"executionTimestamp\":\""+executionTimestamp+"\",\"timeTaken\":\""+responseTime+"\"");
HttpPost request = new HttpPost("http://servername/transactionrecorder/");
request.addHeader("content-type", "application/json");
request.setEntity(params);
HttpResponse response = httpClient.execute(request);
log.info("response :" +response);
}catch(Exception e){
log.info("ExceptionKPI :" +e);
}
Error Response:
jmeter.util.BeanShellTestElement: response :HttpResponseProxy{HTTP/1.1 415
[Accept: application/octet-stream, text/plain;charset=ISO-8859-1, application/xml, text/xml,
application/x-www-form-urlencoded, application/*+xml, multipart/form-data, application/json;charset=UTF-8, application/*+json;charset=UTF-8, */*, Content-Type: text/html;charset=utf-8, Content-Language: en, Content-Length: 1089, Date: Thu, 28 Sep 2017 09:46:01 GMT] ResponseEntityProxy{[Content-Type: text/html;charset=utf-8,Content-Length: 1089,Chunked: false]}}
You need to send a valid JSON with curly brackets {}, for example:
StringEntity params =new StringEntity("{\"testId\":\""+testId+"\",\"TransactionName\":\""+Transaction+"\",\"applicationName\":\""+applicationName+"\",\"conversationId\":\""+conversationId+"\",\"size\":\""+size+"\",\"status\":\""+Status+"\",\"messages\":\""+msg+"\",\"executionTimestamp\":\""+executionTimestamp+"\",\"timeTaken\":\""+responseTime+"\"}");

Dart: Get POST parameters on server side

I send some data from client side using POST request
var value = new Map<String, String>();
value["param1"] = 'value1';
value["param2"] = 'value2';
value["param3"] = 'value3';
HttpRequest.postFormData('http://localhost:8080/', value);
and try to get this data on the server side:
HttpServer.bind(InternetAddress.ANY_IP_V6, 8080).then((server) {
server.listen((HttpRequest request) {
//TODO: process POST request
});
});
But how can I get POST values from the request as Map< string, string>?
upd 1
But as I see result of
var jsonString = await request.transform(UTF8.decoder).join();
depends on type of post message. If I change it result will be
multipart/form-data
------WebKitFormBoundaryoQQD7N0iA5zS8qmg
Content-Disposition: form-data; name="param1"
value 1
------WebKitFormBoundaryoQQD7N0iA5zS8qmg
Content-Disposition: form-data; name="param2"
value 2
------WebKitFormBoundaryoQQD7N0iA5zS8qmg
Content-Disposition: form-data; name="param3"
value 3
------WebKitFormBoundaryoQQD7N0iA5zS8qmg--
text/plain
param1=value 1
param2=value 2
param3=value 3
application/x-www-form-urlencoded
param1=value+1&param2=value+2&param3=value+3
As I have already asked how can I convert it to Map< string, string>?
Here is a complete tutorial https://www.dartlang.org/docs/tutorials/httpserver/#handling-post
String jsonString = await request.transform(UTF8.decoder).join();
or
Map result = await request.transform(UTF8.decoder).join().then(JSON.decode);
I have the same question, and I didn't find any solution until now. I need to pass a map from client to server, and server to use that map to interrogate a mongodb database. Client send that map, but server receive a string. Any conversion to json return also string, not a map.
On the client side I send a map named query:
await HttpRequest.postFormData('http://localhost:8085/$_coll',query).then((HttpRequest response)
On the server side :
if (request.method == 'POST') {
query = await request.transform(utf8.decoder).join();
}
I've tried to encode/decode to json, but with no success.

get value from formdata from MultipartDataStream not working

POST http://myserver.com/QCCSvcHost/MIME/RealtimeTrans/ HTTP/1.1
Content-Type: multipart/form-data; boundary="XbCY" Host: na-w-lxu3
Content-Length: 1470 Expect: 100-continue Connection: Keep-Alive
--XbCY Content-Type: text/plain; charset=utf-8 Content-Disposition: form-data; name=PayloadType
X12_270_Request_005010X279A1
--XbCY Content-Type: text/plain; charset=utf-8 Content-Disposition: form-data; name=ProcessingMode
RealTime
--XbCY Content-Type: text/plain; charset=utf-8 Content-Disposition: form-data; name=PayloadID
e51d4fae-7dec-11d0-a765-00a0c91e6fa6
My below code only gives name in the above case name = "payloadID" but I don't get value which must be "e51d4fae-7dec-11d0-a765-00a0c91e6fa6"
My code:
var provider = await Request.Content.ReadAsMultipartAsync<MemoryStreamDataProvider>(new MemoryStreamDataProvider());
//access form data
NameValueCollection formData = provider.FormData;
//access files
IList<HttpContent> files = provider.Files;
//Example: reading a file's stream like below
HttpContent file1 = files[0];
Stream file1Stream = await file1.ReadAsStreamAsync();
The formData has array of name payloadID, realtime etc but not the value.

Resources