I know how to upload multipart file from postman, but how to do the same through REST API. The consumer API works fine when I hit through postman, but while doing the same through REST, it does not work.
Same thing I am doing through REST like this but its not working:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
String serverUrl = "http://localhost:9001/communication/api/messageEngine/event/RECIPT/sendEmailAttachment";
ParameterizedTypeReference<ApiResponse<Map<String,Object>>> parameterizedTypeReference =
new ParameterizedTypeReference<com.loylty.dataacquisition.model.ApiResponse<Map<String,Object>>>() {};
RestTemplate restTemplate = new RestTemplate();
try {
ResponseEntity<com.loylty.dataacquisition.model.ApiResponse<Map<String,Object>>> result =
restTemplate.exchange(serverUrl, HttpMethod.POST, requestEntity, parameterizedTypeReference);
if (result.getStatusCode().is2xxSuccessful() == false) {
throw new DENotReachableException();
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
Target API or consumer API:
#CrossOrigin
#RequestMapping(method = RequestMethod.POST, value = "/event/{event}/sendEmailAttachment", consumes = {"multipart/form-data"})
public ApiResponse<Object> sendReceiptWithAttachment(#RequestPart("file") MultipartFile file, #PathVariable("event") String event) {
LidsysUtil.messageId.set(String.valueOf(new Date().getTime()));
MessageTracker tracker = new MessageTracker(LidsysUtil.messageId.get(), event);
LidsysUtil.tracker.set(tracker);
LOGGER.info("Executing Message Id : {} ", LidsysUtil.messageId.get());
LOGGER.info("Request received for event : {}", event);
// LOGGER.info("Request Body : {}", LidsysUtil.displayJSON(requestBody));
Map<String, Object> request = messageEngineService.initiateEmailwithAttachmentV2( file, event);
return new ApiResponse<>(APIResponseKey.ALL_GOOD, messageEngineService.execute(request, event), null);
}
Following exception I get when I try with REST api
Exception on source microservice
020-10-21 19:11:18,237 [ERROR]---[DirectJDKLog.java]---[http-nio-8010-exec-2]: Servlet.service() for servlet [dispatcherServlet] in context with path [/dataacquisition] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 400 null] with root cause
org.springframework.web.client.HttpClientErrorException: 400 null
Exception on target microservice
2020-10-21 19:11:17,445 [ERROR]---[HttpLoggingFilter.java]---[http-nio-9001-exec-10]: null
When you are instantiating the ParameterizedTypeReference object why do you refer to the ApiResponse in two different ways? (You use both the simple name and later the full qualified name com.loylty.dataacquisition.model.ApiResponse) Could that mean that these may be two different classes?
Also, on the consumer side, you have declared the sendReceiptWithAttachment() method to return ApiResponse<Object>, rather than ApiResponse<Map<String,Object>>.
Make sure both the consumer and the producer are in sync.
I found the solution, here in map I was adding File instead it required of type Resource
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", file);
Solution
Resource rfile = new FileSystemResource(file)
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", rfile );
Related
I am trying to consume a rest api to upload a Multipart file. I have set MultipartFile in UploadFIleObject and I am sending the same to rest api. However when trying to invoke a rest API as below, I am getting error No serializer found for class java.io.ByteArrayInputStream
public class UploadFileObject implements Serializable{
MultipartFile file;
//getters and setters
}
public void uploadFile() {
Path path = Paths.get("C:/Accounts.txt");
byte[] content = null;
try{
content = Files.readAllBytes(path);
} catch(final IOException e){
}
MultipartFile file = new MockMultipartFile("ABC.txt","ABC.txt","text/plain", content);
UploadFileObject obj = new UploadFileObject();
obj.setFile(file)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set(MediaType.MULTIPART_FORM_DATA);
headers.setContentType("Accept", Mediatype.APPLICATION_JSON_VALUE);
headers.setContentType("Content-type", Mediatype.APPLICATION_JSON_VALUE);
HttpEntity<?> requestEntity = new HttpEntity<>(obj, headers);
String result = getRestTemplate().postForEntity("url", requestEntity, String.class);
}
in application.yml
I have set
spring.serialization.fail-on-empty-beans: false
and in pom.xml I have below of version 2.12.3
jackson-annotation
jackson-core
jackson-databind
jackson-dataformat
jackson-dataformat-xml
}
I'm experiencing some troubles with a simple matter.
I'm trying to send a request to other REST service
//getting restTemplate from RestTemplateBuilder.build()
//endpoint and rest of variables came in properties
Map<String, String> map = new HashMap<>();
map.put("app", app);
map.put("username", username);
map.put("password", password);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String token = restTemplate.postForObject(loginEndpoint, headers, String.class, map);
And I recive:
Unexpected error occurred in scheduled task.
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
The weird thing, when I use a simple CURL call and works smooth.
Already checked the variables and endpoint, and it's correct.
In this case, endpoint must have appropiate placeholders on end point url.
I made this method to do it easy:
private String placeHolders(Map<String, String> values){
String response = "?";
boolean first = true;
for(Map.Entry<String, String> entry:values.entrySet()){
if(first){
first = false;
}else{
response+="&";
}
response+=entry.getKey()+"="+entry.getValue();
}
return response;
}
And the call now Is:
String token = restTemplate.postForObject(loginEndpoint+placeHolders, headers, String.class, map);
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.
I'm using spring 4.2.0 and nifi 0.6.0, when i use RestTemplate call restful api for disabled nifi's controller-services
//get nifi revision
public RevisionDTO getRevision() {
Entity revision = restTemplate.getForObject("http://localhost:8083/nifi-api//controller/revision", Entity.class);
return revision.getRevision();
}
//http parameter
protected Map<String, Object> getRevisionUrl(RevisionDTO revision) {
Map<String, Object> urlVar = new HashMap<String, Object>();
urlVar.put("version", revision.getVersion());
urlVar.put("clientId", revision.getClientId());
return urlVar;
}
//disable controller service
private void disable(ControllerServiceEntity cs) {
Map<String, Object> urlVar = getRevisionUrl(getRevision());
urlVar.put("state", STATE_DISABLED);
restTemplate.put("http://localhost:8083/nifi-api/controller/controller-services/node/" + cs.getControllerService().getId(), null, urlVar);
}
i got the following error log:
org.springframework.web.client.HttpClientErrorException: 409 Conflict
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:636)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:592)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:560)
nifi log
2016-04-11 16:32:53,982 DEBUG [NiFi Web Server-90 - /nifi-api/controller/controller-services/node/5d99ca33-78f6-465e-9ab1-8c7cb5650025] o.a.n.w.StandardOptimisticLockingManager Revision check failed because current revision is [23, 5d4fe4d3-2f32-4a75-899a-458e8e62efce] but supplied revision is [null, 15976fcf-e01b-483c-994b-df812293d7ad]
......
2016-04-11 16:32:53,990 DEBUG [NiFi Web Server-90 - /nifi-api/controller/controller-services/node/5d99ca33-78f6-465e-9ab1-8c7cb5650025] c.s.j.spi.container.ContainerResponse Mapped exception to response: 409 (Conflict)
org.apache.nifi.web.InvalidRevisionException: This NiFi instance has been updated by 'anonymous'. Please refresh to synchronize the view.
at org.apache.nifi.web.StandardOptimisticLockingManager.checkRevision(StandardOptimisticLockingManager.java:62) ~[nifi-web-optimistic-locking-0.6.0.jar:0.6.0]
at org.apache.nifi.web.StandardOptimisticLockingManager.configureFlow(StandardOptimisticLockingManager.java:80) ~[nifi-web-optimistic-locking-0.6.0.jar:0.6.0]
at org.apache.nifi.web.StandardNiFiServiceFacade.updateControllerService(StandardNiFiServiceFacade.java:1714) ~[classes/:0.6.0]
at org.apache.nifi.web.StandardNiFiServiceFacade$$FastClassBySpringCGLIB$$358780e0.invoke() ~[classes/:0.6.0]
browser call disabled api screenshot
is there any way to fix the error?
#Matt Gilman i have changed disable function in two ways:
private void disable(ControllerServiceEntity cs) {
//http form
/*MultiValueMap<String, String> urlVar = getRevisionUrl(getRevision());
urlVar.add("state", STATE_DISABLED);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);*/
//http json
ControllerServiceEntity csu = new ControllerServiceEntity();
csu.setRevision(getRevision());
csu.setControllerService(cs.getControllerService());
csu.getControllerService().setState(STATE_DISABLED);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(JSONUtils.obj2json(csu), requestHeaders);
restTemplate.put(url() + cs.getControllerService().getId(), requestEntity);
}
but i got the same error in nifi:
java.lang.IllegalStateException: DBCPConnectionPool[id=fecc88a8-379d-4fb3-b880-28c5f5caef23] cannot be updated because it is not disabled
at org.apache.nifi.controller.service.StandardControllerServiceNode.verifyCanUpdate(StandardControllerServiceNode.java:217) ~[nifi-framework-core-0.6.0.jar:0.6.0]
at org.apache.nifi.web.dao.impl.StandardControllerServiceDAO.verifyUpdate(StandardControllerServiceDAO.java:225) ~[classes/:na]
at org.apache.nifi.web.dao.impl.StandardControllerServiceDAO.updateControllerService(StandardControllerServiceDAO.java:101) ~[classes/:na]
solved
i compare browser disable request and read api doc again, found disable a controller service must follow these steps:
stop controller service reference
disable controller service reference
set controller service disable state
now my new disable function is
private void disable(ControllerServiceEntity cs) {
//http form
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//stop controller service reference
MultiValueMap<String, String> urlVar = getRevisionUrl(getRevision());
urlVar.add("state", STATE_STOPPED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
restTemplate.put(url() + cs.getControllerService().getId() + "/references", requestEntity);
//disable controller service reference
urlVar = getRevisionUrl(getRevision());
requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
urlVar.add("state", STATE_DISABLED);
restTemplate.put(url() + cs.getControllerService().getId() + "/references", requestEntity);
//set controller service disable state
urlVar = getRevisionUrl(getRevision());
requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
urlVar.add("state", STATE_DISABLED);
restTemplate.put(url() + cs.getControllerService().getId(), requestEntity);
}
It appears the revision is not being interpreted when your request is received. I've seen this once before when the Content-Type of the request was not set correctly and as a result, the revision was not properly parsed.
I'm not super familiar with RestTemplate but the request coming from your browser's Developer Tools has a Content-Type of application/x-www-form-urlencoded. Can you check that RestTemplate is issuing the same type of request?
Alternatively, you could issue the request using the Object's directly and use application/json. While a little different here is an example of setting Processor scheduled state using the client DTOs [1].
Also, at the bottom of this article is a great sequence diagram of the revision use case [2].
[1] https://github.com/apache/nifi/blob/master/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.java#L423
[2] https://community.hortonworks.com/content/kbentry/3160/update-nifi-flow-on-the-fly-via-api.html
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);