Create mock server to test on result of RestTemplate - spring-boot

I am not sure if it is possible to write a Test case that can mock the "http://localhost:8888/setup" site, so the above code can hit it and I want to check if the "http://localhost:8888/setup" received the inputStream correctly.
InputStream inputStream = //got the inputStream;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
InputStreamResource inputStreamResource = new InputStreamResource(inputStream){
#Override
public String getFilename(){
return filename;
}
#Override
public long contentLength(){
return -1;
}
}
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>():
body.add("file", inputStreamResource);
HttpHeader headers = new HttpHeader();
headers.setContentType(MediaType.MULTIPART_FORM_DATA)LinkedMultiValueMap
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String url = "http://localhost:8888/setup";
restTemplate.postForObject(url, requestEntity, String.class);

Try using Wiremock!
Many ways of using it, back then when I used it, I used to run a JAR (wiremock jar) and it spawns up a program on your localhost with your port specified. Henceforth, you can test by hitting that localhost on the port it's up!
For reference check this out :
https://www.softwaretestinghelp.com/wiremock-tutorial/
https://www.baeldung.com/introduction-to-wiremock
https://github.com/wiremock/wiremock

Related

RestTemplate - 415 UNSUPPORTED_MEDIA_TYPE Error

I want to call a REST service from my application. I'm not passing any request to this call. But I can't figure out from where it comes from?
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", bonitaUsername);
map.add("password", bonitaPassword);
map.add("redirect", "false");
HttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
HttpMessageConverter stringHttpMessageConverternew = new StringHttpMessageConverter();
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(formHttpMessageConverter);
converters.add(stringHttpMessageConverternew);
restTemplate.setMessageConverters(converters);
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(map, headers);
ResponseEntity<?> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
List<String> cookies = response.getHeaders().get("Set-Cookie");
String cookieString = "";
for (String cookie : cookies) {
System.out.println();
cookieString += cookie.split(";")[0] + "; ";
}
return cookieString;
This happens due to the mismatch of content types which passing to REST template. Please make sure to send the correct content type. Some times we are receiving JSON request but we need to send as another format.

Spring Webclient multipart/form-data request

I am new to Java (Spring Boot), and i am trying to send a multipart/form-data POST request to s3 to upload a file.
I managed to do this using spring's RestTemplate like this :
public String uploadFile(byte[] file, Map<String, Object> fields, String url) throws URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> formData= new LinkedMultiValueMap<String, Object>();
for (Map.Entry<String, Object> entry : fields.entrySet()) {
formData.add(entry.getKey(), entry.getValue());
}
formData.add("file", file);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(formData, headers);
String response = restTemplate.postForObject(new URI(url), request, String.class);
return response;
}
Then i tried to do the same using webclient, but i can not and AWS respond with The body of your POST request is not well-formed multipart/form-data.
Here is the code using webclient :
public String uploadFileWebc(byte[] file, Map<String, Object> fields, String url) {
MultipartBodyBuilder builder = new MultipartBodyBuilder();
for (Map.Entry<String, Object> entry : fields.entrySet()) {
builder.part(entry.getKey(), entry.getValue(), MediaType.TEXT_PLAIN);
}
builder.part("file", file).filename("file");
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
Void result = webClient.filter(errorHandlingFilter()).build().post().uri(url)
.contentType(MediaType.MULTIPART_FORM_DATA)
.contentLength(file.length)
.bodyValue(parts)
.retrieve()
.bodyToMono(Void.class)
.block();
return "Done Uploading.";
}
Can anybody please point out what am i missing ?
It turns out that webclient does not add the content-length header due to its streaming nature, and S3 API needs this header to be sent.
I ended up using restTemplate for uploading files to S3.

Okhttp create MultipartBody with spring MultipartFile and json object

I have a controller like so that accepts a MultipartFile and json object:
#PostMapping(value = "/v1/submit")
public ResponseEntity submit(
#RequestParam(value="myFile", required = true) MultipartFile myFile
, #Valid #RequestPart(value="fileMeta", required=true) FileMeta fileMeta
){
I need to forward this to a new url using an okhttpclient post with a Multipartbody containing both myFile and fileMeta objects:
OkHttpClient client = new OkHttpClient();
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("myFile", myFile.getName(), okhttp3.RequestBody.create(file, MediaType.parse("pdf"))
.addFormDataPart("fileMeta", fileMeta)
.build();
I am getting following error:
Cannot resolve method 'create(org.springframework.web.multipart.MultipartFile, okhttp3.MediaType)'
The method definition of OkHttp's RequestBody create is the following: create(MediaType contentType, byte[] content). It expects the first the MediaType and second the payload (either as byte[], File or other formats).
So you first have to switch the order of the method arguments and second convert the MultipartFile from Spring to a proper format that the create() method accepts, e.g. byte[] or File:
OkHttpClient client = new OkHttpClient();
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("myFile", myFile.getName(), RequestBody.create(MediaType.parse("pdf"), file)
.addFormDataPart("fileMeta", fileMeta)
.build();
There are already multiple solutions available on StackOverflow to convert MultipartFile to File: How to convert a multipart file to File?
UPDATE: Example for using RestTemplate
#RestController
public class FileSendingController {
#PostMapping("/files")
public void streamFile(#RequestParam("file") MultipartFile file) {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", file);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.postForEntity("http://upload.to", requestEntity, String.class);
}
}

How to send file in JAVA via POST without saving it to disk

I need to send file to a POST service from the server side code.
The content of the file I need to send is in String format.
I don't want to create the file in the disk.
I can't find the way to send file without creating it in the disk.
I prefer not to create a TEMP file but this is what I managed to do.
How do I send file without saving it to disk, not even as TEMP file?
This is the code:
String fileContent = generateFile();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add("apikey","myapikey");
File tmpFile = File.createTempFile("test", ".tmp");
FileWriter writer = new FileWriter(tmpFile);
writer.write(fileContent);
writer.close();
BufferedReader reader = new BufferedReader(new FileReader(tmpFile));
reader.close();
FileSystemResource fsr = new FileSystemResource(tmpFile);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file",fsr);
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
String serverUrl = "https://api.com/api";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity(serverUrl, requestEntity, String.class);
return response.getBody();
POSTMAN screenshot that I use to test the API and it works perfect
Use a ByteArrayResource instead:
String fileContent = generateFile();
ByteArrayResource bar = new ByteArrayResource(fileContent.getBytes());
This way you will not have to create any resource on disk but keep it in memory instead.

Sending a multipart request using RestTemplate

I want to make a multipart request to some external API (created using Spring Boot) but all I get is Required request part 'file' is not present.
I know the source code of the external API but I can't modify it. It looks like this:
#PostMapping("/upload")
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile file){
return ResponseEntity.ok().build();
}
And from my application I create and send requests exactly like on the following snippet:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", "dupa".getBytes());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity("http://api:8080/upload", requestEntity, String.class);
return response.getBody();
What's the reason it doesn't work? The above code rewritten using Apache HttpClient works like charm.
You basically have two options, the solution with byte array:
map.add("file", new ByteArrayResource(byteArrayContent) {
#Override
public String getFilename() {
return "yourFilename";
}
});
I remember having a problem with just adding a byte array, so you need to have a filename too and use ByteArrayResource.
Or adding a File:
map.add("file", new FileSystemResource(file));

Resources