I am working with Spring 3 and RestTemplate. I have basically, two applications and one of them have to post values to the other app. through rest template.
When the values to post are Strings, it's work perfect, but when i have to post mixed and complex params (like MultipartFiles) i get an converter exception.
As example, i have this:
App1 - PostController:
@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute UploadDTO pUploadDTO, BindingResult pResult) throws URISyntaxException, IOException { URI uri = new URI("http://localhost:8080/app2/file/receiver"); MultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>(); mvm.add("param1", "TestParameter"); mvm.add("file", pUploadDTO.getFile()); // MultipartFile Map result = restTemplate.postForObject(uri, mvm, Map.class); return "redirect:postupload"; }
On the other side... i have another web application (App2) that receives the parameters from the App1.
App2 - ReceiverController
@RequestMapping(value = "/receiver", method = { RequestMethod.POST }) public String processUploadFile( @RequestParam(value = "param1") String param1, @RequestParam(value = "file") MultipartFile file) { if (file == null) { System.out.println("Shit!... is null"); } else { System.out.println("Yes!... work done!"); } return "redirect:postupload"; }
My application-context.xml:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /> <bean class="org.springframework.http.converter.FormHttpMessageConverter" /> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /> </list> </property> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> </bean>
Here is the stack of the exception that i am getting when i do the postForObject of the RestTemplate...
org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.commons.CommonsMultipartFile] at org.springframework.http.converter.FormHttpMessageConverter.writePart(FormHttpMessageConverter.java:292) at org.springframework.http.converter.FormHttpMessageConverter.writeParts(FormHttpMessageConverter.java:252) at org.springframework.http.converter.FormHttpMessageConverter.writeMultipart(FormHttpMessageConverter.java:242) at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:194) at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:1) at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:588) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:436) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:415) at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:294) at com.yoostar.admintool.web.UploadTestController.create(UploadTestController.java:86) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:175) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:421) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:409) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:774) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:619)
So my questions are:
We need to create HttpEntitywith header and body. Set the content-type header value to MediaType. MULTIPART_FORM_DATA. When this header is set, RestTemplate automatically marshals the file data along with some metadata.
To pass the Json and Multipart in the POST method we need to mention our content type in the consume part. And we need to pass the given parameter as User and Multipart file. Here, make sure we can pass only String + file not POJO + file. Then convert the String to Json using ObjectMapper in Service layer.
RestTemplate's postForObject method creates a new resource by posting an object to the given URI template. It returns the result as automatically converted to the type specified in the responseType parameter.
A way to solve this without needing to use a FileSystemResource that requires a file on disk, is to use a ByteArrayResource, that way you can send a byte array in your post (this code works with Spring 3.2.3):
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>(); final String filename="somefile.txt"; map.add("name", filename); map.add("filename", filename); ByteArrayResource contentsAsResource = new ByteArrayResource(content.getBytes("UTF-8")){ @Override public String getFilename(){ return filename; } }; map.add("file", contentsAsResource); String result = restTemplate.postForObject(urlForFacade, map, String.class);
I override the getFilename of the ByteArrayResource because if I don't I get a null pointer exception (apparently it depends on whether the java activation .jar is on the classpath, if it is, it will use the file name to try to determine the content type)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With