Spring boot - restTemplate.postForObject - params are null - java

I have 2 spring boot apps running one as a frontend and another as a backend service. From the frontend i make an api call to the backend service and both the parameters that i send show up as null. I think the problem is in the rest template.
UPDATE
So i have noticed if i omit the content value then it works. Since content is the content of a file that is larger than 1mb I added the following to application.yml:
spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB
Here is my code which I updated from one posted in this issue:
How to POST form data with Spring RestTemplate?
But i still don't get the value in the backend controller instead both values are null.
public void upload(byte[] content, String name) {
String encodedString = Base64.getEncoder().encodeToString(content);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("fileName", name);
map.add("content", encodedString);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And here is the controller in the backend. Both fileName and content are null:
#CrossOrigin
#SneakyThrows
#ResponseBody
#PostMapping(value = "/upload")
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] decodedBytes = Base64.getDecoder().decode(form.getContent());
uploadService.upload(decodedBytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Can anyone please see what is wrong with this code?
Thanks in advance.

I guess the problem is that you are trying to use restTemplate.postForObject but with #RequestParam and not a #RequestBody.
In #RequestParam you are expecting the data to be received in the query params /upload?fileName=&content=. But you are actually sending it in the body with the restTemplate.postForObject(backendService+ "/upload", map, String.class);.
So my suggestion is to change
public ResponseEntity<String> upload(#RequestParam(value = "fileName") String fileName, #RequestParam(value = "content") String content)
to
public ResponseEntity<String> upload(#RequestBody Map<String, String> body)
and then get fileName and fileContent from the body.

Ok i could fix it by sending and receiving bytes instead of bytes encoded as string.
So in the resttemplate:
public void upload(byte[] bytes, String name) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("fileName", name);
map.add("bytes", bytes);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
log.debug("map values: " + map.toString());
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And in the controller:
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] bytes = form.getBytes();
uploadService.upload(bytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Still it would be good to know why the previous version didn't work.

Related

What's the best way to encode and decode parameter in springboot?

I use #RequestParam to get the parameter value,but I find the if I pass the value like 'name=abc&def&id=123',I will get the name value 'abc' instead of 'abc&def'. I find the encode and decode the parameter value can solve my problem.But I have to write the encode and decode mehtod in every controller method,Do spring have the global mehtod that decode every #RequestParam value?When using #RequestParam, is it necessary to encode and decode every value?
Here is my code:
#PostMapping("/getStudent")
public Student getStudent(
#RequestParam String name,
#RequestParam String id) {
name= URLDecoder.decode(name, "UTF-8");
//searchStudent
return Student;
}
#PostMapping("/getTeacher")
public teacher getTeacher(
#RequestParam String name,
#RequestParam String teacherNo) {
name= URLDecoder.decode(name, "UTF-8");
//searchTeacher
return teacher;
}
Somebody say the the Spring will have already done this,but I have try,the result is not right.Only use curl cmd is ok,but java code is not ok.
#PostMapping(value = "/example")
public String handleUrlDecode1(#RequestParam String param) {
//print ello%26test
System.out.println("/example?param received: " + param);
return "success";
}
#GetMapping(value = "/request")
public String request() {
String url = "http://127.0.0.1:8080/example?param=ello%26test";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject(url, null, String.class);
}
You must create an HTTP entity and send the headers and parameter in body.
#GetMapping(value = "/request")
public String request() {
String url = "http://127.0.0.1:8080/example";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("param","ello&test");
map.add("id","ab&c=def");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
return restTemplate.postForObject(url, request, String.class);
}
As you can read here, the escape character for & is %26.
So you should use the following
name=abc%26def&id=123
If you don't use an escape character according to URL standards, Spring will try to use what follows & and try to match it as a new query parameter.
No need to manually use URLDecoder, SpringBoot controllers will handle it for you.
#RestController
public class UrlDecodeController {
#GetMapping(value = "/example")
public String handleUrlDecode(#RequestParam String param) {
System.out.println("/example?param received: " + param);
return "success";
}
#PostMapping(value = "/example2")
public String handleUrlDecodeInPostRequest(#RequestParam String param1, ExamplePayload payload) {
System.out.println("/example2?param1 received: " + param1);
System.out.println("request body - value1: " + payload.getValue1());
return "success";
}
#GetMapping(value = "/request")
public String request() {
String url = "http://localhost:8080/example2?param1=test1&test2";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("value1","test1&test2");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
return restTemplate.postForObject(url, request, String.class);
}
class ExamplePayload{
private String value1;
private String value2;
//getters and setters
public ExamplePayload() {
}
}
}
Call with GET /example?param=hello%26test and the System.out.println outputs:
/example?param received: hello&test
Call the POST using curl as an example:
curl -X POST "http://localhost:8080/example2?param1=test1%26test2" -d "value1=test3%26test4"
Prints:
/example2?param1 received: test1&test2
request body - value1: test3&test4
Added GET /request to show using RestTemplate with the application/x-www-form-urlencoded Content-Type. Note that RestTemplate will automatically url encode any values passed as request parameters or in the request body. If you pass a String value of "%26" it will pass it as is, this is what you are seeing in your example. If you pass "&" it will url encode it to "%26" for you, and the Controller decodes it automatically on the other side.

Sending Multipart files with RestTemplate

I have two servers, where server A is sending files to server B. On server B i have an endpoint which recieves files of given category. This is signature of endpoint i need to send files to:
#PostMapping("/uploadMultipleFiles/{projectId}")
public List<UploadFileResponseDts> uploadMultipleFiles(#RequestParam("files") MultipartFile[] files, #RequestParam("categoryId") Long categoryId, #PathVariable("projectId") Long projectId) {
return uploadMulitpleFiles(files, categoryId, projectId);
}
Now im having trouble creating such request with RestTemplate exchange. On Server A i'm trying to send files one by one (endpoint on server B needs to accept multipart array since its used somewhere else as well). This is what i tried:
public Optional<String> uploadFile(File file, Long projectId) throws Exception {
String authToken = getAccessToken();
String projectFileUploadEndpoint = fileUploadEndpoint + SEPARATOR + projectId;
FileInputStream input = new FileInputStream(file);
byte[] bytes = IOUtils.toByteArray(input);
MultiValueMap<String, Object> parts =
new LinkedMultiValueMap<>();
parts.add("files", new ByteArrayResource(bytes));
parts.add("categoryId", 0L);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("Authorization", authToken);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(parts, headers);
ResponseEntity<String> response =
restTemplate.exchange(projectFileUploadEndpoint ,
HttpMethod.POST, requestEntity, String.class);
return Optional.empty();
}
this almost works, the only issue is that on server B files from #RequestParam("files") MultipartFile[] files is always null. I assume that the issue is with the way im assembling parts in on my server A, however i cant find my mistake. Could you point it out?
in your controller all is fine, the problem is about your client code.
Basically you should use FileSystemResource object instead of ByteArrayResource.
The basic motivation is about file metadata. Using FileSystemResource spring is able to rebuild the required MultipartFile info, therefore the your code may be like below
#Test
public void contextLoads()
{
File file = new File("test.txt");
FileSystemResource fileSystemResource = new FileSystemResource(file);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("categoryId", 0L);
parts.add("files", fileSystemResource);
String serviceUrl = "http://localhost:8080/uploadMultipleFiles/0";
RequestEntity<MultiValueMap<String, Object>> requestEntity = post(fromPath(serviceUrl).build().toUri())
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new ResponseErrorHandler()
{
#Override
public boolean hasError(ClientHttpResponse response) throws IOException
{
return false;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException
{
System.out.println(response);
}
});
ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.POST, requestEntity, String.class);
assertThat(200, Is.is(response.getStatusCode()));
}
I hope that this test case can help you to solve the problem

how to do post request with raw data via spring rest template

Can some one tell me how to send a POST request with raw data parameters as in the picture below
i have tried the following code but its not working
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
JsonObject properties = new JsonObject();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
try {
properties.addProperty("app_id", appId);
properties.addProperty("identity","TestAPI");
properties.addProperty("event", "TestCompleted");
properties.addProperty("testType", t.getTestType());
properties.addProperty("testName",t.getTestName());
properties.addProperty("chapter","");
properties.addProperty("module","");
properties.addProperty("pattern",t.getTestPattern());
HttpEntity<String> request = new HttpEntity<>(
properties.toString(), headers);
// params.add("properties", properties.toString());
restTemplate.postForObject(url, request, String.class);
can someone help?
Try this :
#RestController
public class SampleController {
#RequestMapping("/req")
public String performReqest(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
JsonObject properties = new JsonObject();
properties.addProperty("no", "123");
properties.addProperty("name", "stackoverflow");
HttpEntity<String> request = new HttpEntity<>(properties.toString(), headers);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:4040/student", request, String.class);
return "Response from Server is : "+response;
}
#RequestMapping("/student")
public String consumeStudent(#RequestBody Student student){
System.out.println(student);
return "Hello.."+student.name;
}
}
class Student{
public int no;
public String name;
public Map<String,String> properties;
}
Don't forgot to move Student class and change all field to private with require getters and setters.
Above code is only for demo purpose.
Please try with this:
ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
Did u tried using postmaster and checked the output first. If its working then you can go for post or exchange method. exchange returns and post dont.
try this:
URI uri = new URI("something");
Map<String, Object> params = new HashMap<>();
params.put("app_id", "something");
params.put("identity", something);
HttpEntity<Map<String, String>> request = new HttpEntity(params , headers);
ResponseEntity<String> response = restTemplate.postForEntity(uri, request, String.class);

Spring Framework RestClientException: Extract String from text/plain response

I have a REST API with this endpoint:
#GET
#Path("rol/{codEmp}")
#Produces(MediaType.TEXT_PLAIN)
public String getRole(#PathParam("codEmp") Long codEmp) {
return dao.getRole(codEmp);
}
An example of response can be: HOUSEKEEPER.
I consume it this way:
#Override
public String getRole(Long codEmp) {
HashMap<String, Object> urlVariables = new HashMap<String, Object>();
urlVariables.put("codEmp", codEmp);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Collections.singletonList(MediaType.parseMediaType("text/plain")));
HttpEntity<Object> requestEntity = new HttpEntity<Object>(httpHeaders);
return restTemplate.exchange(rootUrl.concat("/rol/{codEmp}"), HttpMethod.GET, requestEntity, String.class, urlVariables).getBody();
}
But I get this error:
"Could not extract response: no suitable HttpMessageConverter found
for response type [java.lang.String] and content type [text/plain]"
I know the right way is to send a JSON response but I have to do it with raw String.
How can I solve it?
Thanks
Solved. I have added an String converter to my Rest Template:
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

How to call MultipartFile Spring REST URL with RestTemplate

When I try to call following MultipartFile Spring REST url with my Spring Template base Test method, I got following exception. How can I make this correct. Thanks.
Spring REST URL:
#RequestMapping(value = "/media/uploadMultipartFile/{token}/{title}/{trailId}/{wpId}", method = RequestMethod.POST)
public #ResponseBody MediaHttp uploadMultipartFile(#RequestParam MultipartFile file,
#PathVariable String token,
#PathVariable String title,
#PathVariable String trailId,
#PathVariable String wpId,
HttpServletResponse response)
Test method:
try {
// Message Converters
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new FormHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new MappingJacksonHttpMessageConverter());
// RestTemplate
RestTemplate template = new RestTemplate();
template.setMessageConverters(messageConverters);
// URL Parameters
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("token", "nkc2jvbrbc");
parts.add("title", "test mp4 file");
parts.add("trailId", "2");
parts.add("wpId", "7");
parts.add("file", new FileSystemResource("C:\\Users\\Public\\Pictures\\Sample Pictures\\test.mp4"));
// Post
MediaHttp result = template.postForObject(Constants.APPLICATION_URL + "/media/uploadMultipartFile/{token}/{title}/{trailId}/{wpId}", parts, MediaHttp.class);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
Exception:
Invalid amount of variables values in [http://test.com:8080/DMW-skeleton-1.0/media/uploadMultipartFile/{token}/{title}/{trailId}/{wpId}]: expected 4; got 0
The message is pretty clear, you don't specify any path parameters for submission. You only provide a map which will be send as the body of the request.
change your call to include those parameters as the last part of the method call.
// URL Parameters
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", new FileSystemResource("C:\\Users\\Public\\Pictures\\Sample Pictures\\test.mp4"));
// Post
MediaHttp result = template.postForObject(Constants.APPLICATION_URL + "/media/uploadMultipartFile/{token}/{title}/{trailId}/{wpId}", parts, MediaHttp.class, "nkc2jvbrbc", "test mp4 file", "2", "7);

Categories

Resources