Rest template for postForEntity with file & other properties - java

I want to upload the file with Json request in rest template along with other properties. But I couldn't able to do this.
#Bean
public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
#Autowired
private RestTemplate restTemplate;
#Scheduled(fixedDelay = 1000)
public void _do() throws Exception {
HashMap<String, String> documentProperties = new HashMap<>();
documentProperties.put("number", "123");
MultipartFile file = Somefile;
UploadDocumentRequest uploadDocumentRequest = new UploadDocumentRequest();
uploadDocumentRequest.setDocumentClass("DocClass");
uploadDocumentRequest.setDocumentProperties(documentProperties);
uploadDocumentRequest.setFile(file); ----???
ResponseEntity<String> value = restTemplate.postForEntity("URL", uploadDocumentRequest, String.class);
}

You have to create HttpEntity with header and body.
Set the content-type header value to MediaType.MULTIPART_FORM_DATA.
Build the request body as an instance of LinkedMultiValueMap class.
Construct an HttpEntity instance that wraps the header and the body object and post it using a RestTemplate.
A sample code is shown as follows:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", getFileToBeUploaded());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(serviveUrl, requestEntity, String.class);

Related

How to Spring Retryable with http status code

I'm currently using spring 2 with restTemplate and I would like to have a retry sending a post request only with a certain status codes or any of code 500.
How do I do this?
Here is my code
#Retryable(value = RestClientException.class, exclude = {UnknownHostException.class},
backoff=#Backoff(delayExpression = 10000,
multiplierExpression = 2,
maxDelayExpression = 50000))
public HttpStatus postRequest(final File file) throws RestClientException {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new FileSystemResource(file));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("Token", mytoken);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange(URI, HttpMethod.POST,requestEntity, String.class);
return response.getStatusCode();
}

Consuming HTTP POST method which is secured with Basic Auth in spring

I want to consume a post method which is secured using basicAuth. I am using springBoot, Spring RestTemplate to consume it.
I've tried like this:
#CrossOrigin(origins = "*", maxAge = 3600)
#RequestMapping(value = "/ValidateAnswers", method = RequestMethod.POST)
public ResponseEntity<String> ValidateAnswers(#RequestBody Object requestIbject,
HttpServletRequest request, HttpServletResponse response) {
final String uri = "foo:8080//validateAnswers";
// hiding full path here,
RestTemplate restTemplate = new RestTemplate();
String plainClientCredentials = "user:pass";
String base64ClientCredentials = new String(
Base64.encodeBase64(plainClientCredentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64ClientCredentials);
HttpEntity<String> entity = new HttpEntity<String>("parameters",
headers);
ResponseEntity<String> respEntity = restTemplate.postForObject(uri, requestIbject, String.class);
// .exchange(uri,
// HttpMethod.POST, entity, String.class);
System.err.println("=------------Response--------------");
System.err.println("----" + respEntity);
return respEntity;
}
How do I pass the headers with basicAuth and post request body ?
if you using spring > 4.3.1
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("user", "password"));
From your code snippets I don't think you should use post method, try get first. And refer to my previous test:
public class Test {
private HttpHeaders getHeaders(){
String plainCredentials="admin:admin";
String base64Credentials = Base64.getEncoder().encodeToString(plainCredentials.getBytes());
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Credentials);
return headers;
}
#Test
public void testLogin() {
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request = new HttpEntity<String>(getHeaders());
ResponseEntity<String> response = restTemplate.exchange("http://localhost:8080/login", HttpMethod.GET,
request, String.class);
System.out.println(response.getBody());
}
}

Sending file over spring rest service via resttemplate

Title might look common but none of them fit in my issue.
I have a rest service which accept normal parameters and file in form of multipart.
i want to use resttemplate to send data and file to above rest service.
till the time i was sending normal string data there was no issue. once i add code of sending bytes then i start getting 400 Bad request error.
if i comment code to send ByteArrayResource then it start working for normal parameters.
below is sample code
Rest service controller
#RestController
#RequestMapping(value="/ticket")
public class UserTicketController {
#RequestMapping(value="/createTicket.do",method={RequestMethod.POST},
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE},headers={"content-type="+MediaType.MULTIPART_FORM_DATA_VALUE})
public void createTicket(#ModelAttribute ServiceDeskRequest serviceDeskRequest, HttpServletRequest request,HttpServletResponse response) throws Exception{
}
}
Servicedeskrequest model attribute is
public class ServiceDeskRequest implements Serializable{
private String jsonData;
private MultipartFile attachment;
}
application-context.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
Client Side code
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, Object> requestParamerterMap = new LinkedMultiValueMap<String, Object>();
requestParamerterMap.add("jsonData", jsonData);
MultipartFile attachment = userRequest.getAttachment();
if(attachment!=null && attachment.getOriginalFilename()!=null) {
ByteArrayResource byteArrayResource = new ByteArrayResource(attachment.getBytes(), attachment.getOriginalFilename());
requestParamerterMap.add("attachment", byteArrayResource);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(requestParamerterMap, headers);
String response = restTemplate.postForObject(targetUrl, requestEntity, String.class);
I figured it out. There are two piece in this puzzle. No change in service code.
Providing right converter to resttemplate. In list of default converts spring doesn't add FormHttpMessageConverter.
FormHttpMessageConverter converter = new FormHttpMessageConverter();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(converter);
overriding bytearrayresource class. plz note you need to override getFilename method so that document name can be received at service side.
public class MultipartByteArrayResource extends ByteArrayResource{
private String fileName;
public MultipartByteArrayResource(byte[] byteArray) {
super(byteArray);
}
public String getFilename() {
return fileName;
}
public void setFilename(String fileName) {
this.fileName= fileName;
}
}
After above changes client code will be
FormHttpMessageConverter converter = new FormHttpMessageConverter();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(converter);
MultiValueMap<String, Object> requestParamerterMap = new LinkedMultiValueMap<String, Object>();
requestParamerterMap.add("jsonData", jsonData);
MultipartFile attachment = userRequest.getAttachment();
if(attachment!=null && attachment.getOriginalFilename()!=null) {
//ByteArrayResource byteArrayResource = new ByteArrayResource(attachment.getBytes(), attachment.getOriginalFilename());
MultipartByteArrayResource resource = new MultipartByteArrayResource(attachment.getBytes());
//pass file name sepratly
resource.setFilename(attachment.getOriginalFilename());
requestParamerterMap.add("attachment", resource);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(requestParamerterMap, headers);
String response = restTemplate.postForObject(targetUrls.get("sdCreateTicketsUrl"), requestEntity, String.class);
First, value="/createTicket.do" is way off the REST convention. Same goes for /ticket.
Creation of a ticket should be done by POST to URL: .../tickets/

Spring - RestTemplate - Multipart File

This controller works fine
#Controller
public class FileUploadController {
....
#PostMapping("/convert")
public void fileUpload(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes, HttpServletResponse response) {
Now i want to call this controller from another spring project via RestTemplate. I tried many things, but noting works. Here is my last code:
#Controller
public class FileController {
....
#PostMapping("/convert")
public void fileUpload(#RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes,
HttpServletResponse response) throws Exception {
ArrayList<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(
Arrays.asList(new FormHttpMessageConverter(),new MappingJackson2HttpMessageConverter(), new ResourceHttpMessageConverter()));
RestTemplate template = restTemplate();
template.setMessageConverters(converters);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();
multipartRequest.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequest, header);
template.postForObject("http://localhost:8080/convert", requestEntity, String.class);
}
I if call FileUploadController (via postman) it works. If if call FileController
i get this Exception
"exception":
"org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile]",
"path": "/convert"
Take a look at the answer here, it should be exactly what you are looking for: Attempting to test rest service with multipart file
The issue there is about posting a multi-part file to a rest service using a RestTemplate.
Basically, what you have to do is to simulate a file upload. You can try something like this:
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new FileSystemResource("file.jpg"));
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "multipart/form-data");
headers.set("Accept", "text/plain");
String result = restTemplate.postForObject(
"http://host:port/path",
new HttpEntity<MultiValueMap<String, Object>>(parameters, headers),
String.class);

Integration test with TestRestTemplate for Multipart POST request returns 400

I know that similar question has been here already couple of times but following suggested fixes did not solve my problem.
I have a simple controller with the following endpoint:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> singleFileUpload(#RequestParam("file") MultipartFile file) {
log.debug("Upload controller - POST: {}", file.getOriginalFilename());
// do something
}
I am trying to write an integration test for it using Spring TestRestTemplate but all of my attemps end with 400 - Bad Request (no logs clarifying what went wrong in console).
The log inside the controller did not get hit so it failed before getting there.
Could you please take a look on my test and suggest what am I doing wrong?
#Test
public void testUpload() {
// simulate multipartfile upload
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("image.jpg").getFile());
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", file);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(parameters, headers);
ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");
// Expect Ok
assertThat(response.getStatusCode(), is(HttpStatus.OK));
}
I tried the following:
#Test
public void testUpload() {
LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new org.springframework.core.io.ClassPathResource("image.jpg"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<LinkedMultiValueMap<String, Object>>(parameters, headers);
ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");
// Expect Ok
assertThat(response.getStatusCode(), is(HttpStatus.OK));
}
As you can see I used the org.springframework.core.io.ClassPathResource as object for the file and ti worked like a charm
I hope it's useful
Angelo
FileSystemResource also could be used in case if you want to use java.nio.file.Path.
Package: org.springframework.core.io.FileSystemResource
For example, you could do this:
new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"))
Full code example:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UploadFilesTest {
private final TestRestTemplate template;
#Autowired
public UploadFilesTest(TestRestTemplate template) {
this.template = template;
}
#Test
public void uploadFileTest() {
var multipart = new LinkedMultiValueMap<>();
multipart.add("file", file());
final ResponseEntity<String> post = template.postForEntity("/upload", new HttpEntity<>(multipart, headers()), String.class);
assertEquals(HttpStatus.OK, post.getStatusCode());
}
private HttpHeaders headers() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
return headers;
}
private FileSystemResource file() {
return new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"));
}
}
Rest controller:
#RestController
public class UploadEndpoint {
#PostMapping("/upload")
public void uploadFile(#RequestParam("file") MultipartFile file) {
System.out.println(file.getSize());
}
}

Categories