I have a Java Spring MVC Web application as server. And AngularJS based application as client.
In AngularJS, I have to upload a file and send to server.
Here is my html
<form ng-submit="uploadFile()" class="form-horizontal" enctype="multipart/form-data">
<input type="file" name="file" ng-model="document.fileInput" id="file" onchange="angular.element(this).scope().setTitle(this)" />
<input type="text" class="col-sm-4" ng-model="document.title" id="title" />
<button class="btn btn-primary" type="submit">
Submit
</button>
</form>
Here is my UploadController.js
'use strict';
var mainApp=angular.module('mainApp', ['ngCookies']);
mainApp.controller('FileUploadController', function($scope, $http) {
$scope.document = {};
$scope.setTitle = function(fileInput) {
var file=fileInput.value;
var filename = file.replace(/^.*[\\\/]/, '');
var title = filename.substr(0, filename.lastIndexOf('.'));
$("#title").val(title);
$("#title").focus();
$scope.document.title=title;
};
$scope.uploadFile=function(){
var formData=new FormData();
formData.append("file",file.files[0]);
$http({
method: 'POST',
url: '/serverApp/rest/newDocument',
headers: { 'Content-Type': 'multipart/form-data'},
data: formData
})
.success(function(data, status) {
alert("Success ... " + status);
})
.error(function(data, status) {
alert("Error ... " + status);
});
};
});
It is going to the server. Here is my DocumentUploadController.java
#Controller
public class DocumentUploadController {
#RequestMapping(value="/newDocument", headers = "'Content-Type': 'multipart/form-data'", method = RequestMethod.POST)
public void UploadFile(MultipartHttpServletRequest request, HttpServletResponse response) {
Iterator<String> itr=request.getFileNames();
MultipartFile file=request.getFile(itr.next());
String fileName=file.getOriginalFilename();
System.out.println(fileName);
}
}
When I run this I get the following exception
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found] with root cause
org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:954)
at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:331)
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:351)
at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:156)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1047)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:892)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:801)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
In my applicationContext.xml, I have mentioned
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000" />
</bean>
I am using
spring - 3.2.1.RELAESE
commons-fileupload - 1.2.2
commons-io - 2.4
How to solve this?
It would be great if anyone tel me how to send file and other formdata from angularJS and get it in server.
UPDATE 1
#Michael : I can see this only in the console, when I click submit.
POST http://localhost:9000/serverApp/rest/newDocument 500 (Internal Server Error) angular.js:9499
(anonymous function) angular.js:9499
sendReq angular.js:9333
$http angular.js:9124
$scope.uploadFile invoice.js:113
(anonymous function) angular.js:6541
(anonymous function) angular.js:13256
Scope.$eval angular.js:8218
Scope.$apply angular.js:8298
(anonymous function) angular.js:13255
jQuery.event.dispatch jquery.js:3074
elemData.handle
My server is running in other port 8080. I am uisng yeoman,grunt and bower. So thin gruntfile.js I have mentioned the server port. So it goes to server and running that and throws the exception
UPDATE 2
The boundary is not setting
Request URL:http://localhost:9000/serverApp/rest/newDocument
Request Method:POST
Status Code:500 Internal Server Error
Request Headers view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:792
Content-Type:multipart/form-data
Cookie:ace.settings=%7B%22sidebar-collapsed%22%3A-1%7D; isLoggedIn=true; loggedUser=%7B%22name%22%3A%22admin%22%2C%22password%22%3A%22admin23%22%7D
Host:localhost:9000
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
X-Requested-With:XMLHttpRequest
Request Payload
------WebKitFormBoundaryCWaRAlfQoZEBGofY
Content-Disposition: form-data; name="file"; filename="csv.csv"
Content-Type: text/csv
------WebKitFormBoundaryCWaRAlfQoZEBGofY--
Response Headers view source
connection:close
content-length:5007
content-type:text/html;charset=utf-8
date:Thu, 09 Jan 2014 11:46:53 GMT
server:Apache-Coyote/1.1
I faced the same issue and encountered the same issue even after updating the transformRequest. 'Some how, the header boundary doesn't seem to have set correctly.
Following http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs, the problem is resolved. Extract from the location....
By setting ‘Content-Type’: undefined, the browser sets the Content-Type to multipart/form-data for us and fills in the correct boundary. Manually setting ‘Content-Type’: multipart/form-data will fail to fill in the boundary parameter of the request.
Not sure if this helps any one but perhaps makes it easy for people looking at this post... At least, it makes it less difficult.
Introduction
I have had the same problem and found a complete solution to send both json and file from angular based page to a Spring MVC method.
The main problem is the $http which doesn't send the proper Content-type header (I will explain why).
The theory about multipart/form-data
To send both json and file we need to send a multipart/form-data, which means "we send different items in the body separated by a special separator". This special separator is called "boundary", which is a string that is not present in any of the elements that are going to be sent.
The server needs to know which boundary is being used so it has to be indicated in the Content-type header (Content-Type multipart/form-data; boundary=$the_boundary_used).
So... two things are needed:
In the header --> indicate multipart/form-data AND which boundary is used (here is where $http fails)
In the body --> separate each request parameter with the boundary
Example of a good request:
header
Content-Type multipart/form-data; boundary=---------------------------129291770317552
Which is telling the server "I send a multipart message with the next separator (boundary): ---------------------------129291770317552
body
-----------------------------129291770317552 Content-Disposition: form-data; name="clientInfo"
{ "name": "Johny", "surname":"Cash"}
-----------------------------129291770317552
Content-Disposition: form-data; name="file"; filename="yourFile.pdf"
Content-Type: application/pdf
%PDF-1.4
%õäöü
-----------------------------129291770317552 --
Where we are sending 2 arguments, "clientInfo" and "file" separated by the boundary.
The problem
If the request is sent with $http, the boundary is not sent in the header (point 1), so Spring is not able to process the data (it doesn't know how to split the "parts" of the request).
The other problem is that the boundary is only known by the FormData... but FormData has no accesors so it's impossible to know which boundary is being used!!!
The solution
Instead of using $http in js you should use standard XMLHttpRequest, something like:
//create form data to send via POST
var formData=new FormData();
console.log('loading json info');
formData.append('infoClient',angular.toJson(client,true));
// !!! when calling formData.append the boundary is auto generated!!!
// but... there is no way to know which boundary is being used !!!
console.log('loading file);
var file= ...; // you should load the fileDomElement[0].files[0]
formData.append('file',file);
//create the ajax request (traditional way)
var request = new XMLHttpRequest();
request.open('POST', uploadUrl);
request.send(formData);
Then, in your Spring method you could have something like:
#RequestMapping(value = "/", method = RequestMethod.POST)
public #ResponseBody Object newClient(
#RequestParam(value = "infoClient") String infoClientString,
#RequestParam(value = "file") MultipartFile file) {
// parse the json string into a valid DTO
ClientDTO infoClient = gson.fromJson(infoClientString, ClientDTO.class);
//call the proper service method
this.clientService.newClient(infoClient,file);
return null;
}
Carlos Verdes's answer failed to work with my $http interceptor, which adds authorization headers and so on. So I decided to add to his solution and create mine using $http.
Clientside Angular (1.3.15)
My form (using the controllerAs syntax) is assuming a file and a simple object containing the information we need to send to the server. In this case I'm using a simple name and type String property.
<form>
<input type="text" ng-model="myController.myObject.name" />
<select class="form-control input-sm" ng-model="myController.myObject.type"
ng-options="type as type for type in myController.types"></select>
<input class="input-file" file-model="myController.file" type="file">
</form>
The first step was to create a directive that binds my file to the scope of the designated controller (in this case myController) so I can access it. Binding it directly to a model in your controller won't work as the input type=file isn't a built-in feature.
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
Secondly I created a factory called myObject with an instance method create that allows me to transform the data upon invoking create on the server. This method adds everything to a FormData object and converts it using the transformRequest method (angular.identity). It is crucial to set your header to undefined. (Older Angular versions might require something than undefined to be set). This will allow the multidata/boundary marker to be set automatically (see Carlos's post).
myObject.prototype.create = function(myObject, file) {
var formData = new FormData();
formData.append('refTemplateDTO', angular.toJson(myObject));
formData.append('file', file);
return $http.post(url, formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined }
});
}
All that is left to do client side is instantiating a new myObject in myController and invoking the create method in the controller's create function upon submitting my form.
this.myObject = new myObject();
this.create = function() {
//Some pre handling/verification
this.myObject.create(this.myObject, this.file).then(
//Do some post success/error handling
);
}.bind(this);
Serverside Spring (4.0)
On the RestController I can now simply do the following: (Assuming we have a POJO MyObject)
#RequestMapping(method = RequestMethod.POST)
#Secured({ "ROLE_ADMIN" }) //This is why I needed my $httpInterceptor
public void create(MyObject myObject, MultipartFile file) {
//delegation to the correct service
}
Notice, I'm not using requestparameters but just letting spring do the JSON to POJO/DTO conversion. Make sure you got the MultiPartResolver bean set up correctly too and added to your pom.xml. (And Jackson-Mapper if needed)
spring-context.xml
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="268435456" /> <!-- 256 megs -->
</bean>
pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
You can try this
.js
$scope.uploadFile=function(){
var formData=new FormData();
formData.append("file",file.files[0]);
$http.post('/serverApp/rest/newDocument', formData, {
transformRequest: function(data, headersGetterFunction) {
return data;
},
headers: { 'Content-Type': undefined }
}).success(function(data, status) {
alert("Success ... " + status);
}).error(function(data, status) {
alert("Error ... " + status);
});
.java
#Controller
public class DocumentUploadController {
#RequestMapping(value="/newDocument", method = RequestMethod.POST)
public #ResponseBody void UploadFile(#RequestParam(value="file", required=true) MultipartFile file) {
String fileName=file.getOriginalFilename();
System.out.println(fileName);
}
}
That's based on https://github.com/murygin/rest-document-archive
There is a good example of file upload
https://murygin.wordpress.com/2014/10/13/rest-web-service-file-uploads-spring-boot/
Related
I am trying to recreate the spring-boot angularjs example application from here.
When I run the application using ./mnvw spring-boot:run the following error shows:
[ERROR] ERROR in src/app/app.component.html(6,18): : Property 'id' does not exist on type '{}'.
[ERROR] src/app/app.component.html(7,23): : Property 'content' does not exist on type '{}'.
My source code is the same as the link provided above but for clarity, the typescript file:
import { Component } from '#angular/core';
import {HttpClient} from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Demo';
data = {};
constructor(private http: HttpClient) {
http.get('resource').subscribe(data => this.data = data);
}
}
The html file:
<div style="text-align:center"class="container">
<h1>
Welcome {{title}}!
</h1>
<div class="container">
<p>Id: <span>{{data.id}}</span></p>
<p>Message: <span>{{data.content}}</span></p>
</div>
</div>
And the java controller:
#SpringBootApplication
#Controller
public class AngularApplication {
public static void main(String[] args) {
SpringApplication.run(AngularApplication.class, args);
}
#GetMapping("/resource")
#ResponseBody
public Map<String, Object> home() {
Map<String, Object> model = new HashMap<String, Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
}
The app.module.ts file imports HttpClientModule. When I ng serve the application to localhost:4200 the site loads but without data.
The idea is to service the /resource request and return an object with the right keys for the client, but the keys do not exist and/or are not being recognized by the client.
How do I correctly pass the generated id and hello world content to the data object?
There was a tiny difference in package.json that was causing the issue. The one from the provided link uses "build": "ng build" and the default generated one from CLI uses "build": "ng build --prod". After changing this the example is working.
I'm trying to send image by ajax:
function putImage() {
var image = document.getElementById('image').files[0];
var formData = new FormData();
formData.append('image', image);
$.ajax({
url: 'http://localhost:8080/ImageStorageREST/image',
type: 'put',
data: formData,
contentType: false,
processData: false,
async: true,
success: function(data) {
console.log("success");
console.log(data);
},
error: function(data) {
console.log("error");
console.log(data);
}
});
}
HTML form:
<form>
<input type="file" multiple accept="image/*,image/jpeg" id="image"/>
<input type="submit" value="Отправить" onClick="putImage(); return false;" />
</form>
The controller's method:
#RequestMapping(value="/image", method=RequestMethod.PUT)
public #ResponseBody String addImage(#RequestPart("image") MultipartFile image) {
return "RECEIVED";
}
Multipart Resolver is registered in dispatcher config file:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000" />
</bean>
I received org.springframework.beans.BeanInstantiationException at server
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.multipart.MultipartFile]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:101)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveModelAttribute(HandlerMethodInvoker.java:775)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:368)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:172)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:434)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:879)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
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:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:620)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)
And request status is
Request URL:http://localhost:8080/ImageStorageREST/image
Request Method:PUT
Status Code:500 Internal Server Error
Remote Address:[::1]:8080
But I see the parameter at browser:
Content-Disposition: form-data; name="image"; filename="format_jpg.jpg"
Content-Type: image/jpeg
......
So why is this exception thrown? I looked a lot of links where the solution was to add multipartResolver bean, but I have it yet.
This problem was caused by using Servlet 2.5 (Tomcat 6.0). The HttpServletRequest class doesn't contain getParts() method.
So I solved my problem by changing controller's method:
#RequestMapping(value="/image", method=RequestMethod.PUT, consumes="multipart/form-data")
public #ResponseBody void addImage(HttpServletRequest request) throws ImageException {
byte[] bytes = getBytesFromFile(request);
Image image = new Image();
image.setByteData(bytes);
imageService.addImage(image);
}
private byte[] getBytesFromFile(HttpServletRequest request) throws ImageException {
ServletFileUpload upload = new ServletFileUpload();
byte[] bytes = null;
FileItemIterator iter;
try {
iter = upload.getItemIterator(request);
while(iter.hasNext()) {
FileItemStream item = iter.next();
InputStream stream = item.openStream();
bytes = IOUtils.toByteArray(stream);
}
return bytes;
} catch (IOException | FileUploadException e) {
throw new ImageException("The problem while storing file. Try again.",e);
}
}
I want to upload a file with jersey via jquery/AJAX but I dont get it how to get the file from the input and send it with ajax.
Here is my Code so far:
html
<form action="rest/files/upload" method="post" enctype="multipart/form-data">
<p>
Select a file : <input type="file" name="file" size="50" />
</p>
<input type="submit" value="Upload It" />
</form>
jAVA
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader) {
String filePath = SERVER_UPLOAD_LOCATION_FOLDER + contentDispositionHeader.getFileName();
// save the file to the server
saveFile(fileInputStream, filePath);
String output = "File saved to server location : " + filePath;
return Response.status(200).entity(output).build();
}
// save uploaded file to a defined location on the server
private void saveFile(InputStream uploadedInputStream,
String serverLocation) {
try {
OutputStream outpuStream = new FileOutputStream(new File(serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
outpuStream.write(bytes, 0, read);
}
outpuStream.flush();
outpuStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
The code is working with a normal submit in html.
I used this example: jersey file upload
here is my approach so far
var file = $('input[name="file"').get(0).files[0];
var formData = new FormData();
formData.append('file', file);
$.ajax({
url : '/OIS/rest/upload', //Server script to process data
type : 'POST',
data : formData,
cache : false,
contentType : 'multipart/form-data',
dataType : 'application/json',
processData : false,
success : function(data, textStatus, jqXHR) {
var userObj = JSON.parse(jqXHR.responseText);
alert(userObj);
},
error : function(jqXHR, textStatus, errorThrown) {
alert(textStatus);
}
});
but it´s throwing a nullpointer exception
Jan 31, 2015 12:51:46 PM org.apache.catalina.core.StandardWrapperValve invoke
SCHWERWIEGEND: Servlet.service() for servlet [Smart_Office] in context with path [/Smart_Office] threw exception
java.lang.NullPointerException
at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.unquoteMediaTypeParameters(MultiPartReaderClientSide.java:244)
at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readMultiPart(MultiPartReaderClientSide.java:171)
at com.sun.jersey.multipart.impl.MultiPartReaderServerSide.readMultiPart(MultiPartReaderServerSide.java:80)
at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:157)
at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:85)
at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:490)
at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:555)
at com.sun.jersey.multipart.impl.FormDataMultiPartDispatchProvider$FormDataInjectableValuesProvider.getInjectableValues(FormDataMultiPartDispatchProvider.java:122)
at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$EntityParamInInvoker.getParams(AbstractResourceMethodDispatchProvider.java:153)
at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:203)
at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:302)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1542)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1473)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1419)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1409)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:409)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:540)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:715)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
You server side source cose seams judicious as you have follow one of a well written tutorials, but as you have tried to mix code from this and that articles to bring an Ajaxified Upload with Jersey sample, that wouldn't work seamlessly.
The main issue I would note is being with content negociation between your server side endpoint and your client side code.
I won't get into Jersey internal and look for how that NPE would be thrown but I will suggest to update your client Ajax call to omit contentType handling, setting below properties to false:
contentType
processData
and ommitting the dataType property, so that resulting code will look as follows:
var file = $('input[name="file"').get(0).files[0];
var formData = new FormData();
formData.append('file', file);
$.ajax({
url : '/OIS/rest/upload',
type : 'POST',
data : formData,
cache : false,
contentType : false,
processData : false,
success : function(data, textStatus, jqXHR) {
var userObj = JSON.parse(jqXHR.responseText);
alert(userObj);
},
error : function(jqXHR, textStatus, errorThrown) {
alert(textStatus);
}
});
Ok first of all code (its mega simple):
#Controller
#RequestMapping("/")
public class HelloController {
private final static Logger logger = Logger.getLogger(HelloController.class);
#RequestMapping(method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
logger.info("ELO ELO");
model.addAttribute("message", "Hello world!");
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.168.0.200:8000/GPIO/11/function/in";
//String url = "http://192.168.0.200:8000/GPIO/11/function";
//restTemplate.getForObject(url, String.class);
String test = "";
restTemplate.postForObject(url, null, String.class);
logger.info(test);
return "hello";
}
Next example that I'm not a crazy man here is response from postman (chrome):
And at the end full error log:
type Exception report
message Request processing failed; nested exception is
java.lang.IllegalArgumentException: "None" does not contain '/'
description The server encountered an internal error that prevented it
from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
java.lang.IllegalArgumentException: "None" does not contain '/'
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:927)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.IllegalArgumentException: "None" does not contain '/'
org.springframework.http.MediaType.parseMediaType(MediaType.java:697)
org.springframework.http.HttpHeaders.getContentType(HttpHeaders.java:305)
org.springframework.web.client.HttpMessageConverterExtractor.getContentType(HttpMessageConverterExtractor.java:113)
org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:84)
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:492)
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:447)
org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:295)
pl.piquarium.mvc.HelloController.printWelcome(HelloController.java:35)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:439)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:427)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
note The full stack trace of the root cause is available in the Apache
Tomcat/8.0.3 logs.
Request headers:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Cookie:__utma=212787668.2094541430.1400264829.1400264829.1400268775.2; __utmz=212787668.1400264829.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Host:192.168.0.200:8000
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
Response Headers:
Cache-Control:no-cache
Content-Length:2
Content-Type:None
Date:Fri, 16 May 2014 22:37:16 GMT
Server:WebIOPi/0.7.0/Python3.2
Did you try setting headers like below,
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);//or any other required
HttpEntity request = new HttpEntity(null, headers);
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.168.0.200:8000/GPIO/11/function/in";
String response = restTemplate.postForObject(url,request,String.class);
The problem is that the server is returning an invalid content type of None instead of something like text/plain, and Spring REST is choking on it. You will need to add a custom message converter for the None type, not use a typed query and parse the response object yourself, or get the Pi people to fix their broken Web server.
I am working on a spring mvc web application, in which I am using Google Visualization API for generating some charts. I have a model class which contains 2 arraylists, which represent the data that I`m sending to the function that draws the chart (this is what i want to be converted to a JSON).
The model class:
#Component
public class JsonResponse {
private List<Integer> percentages = new ArrayList<Integer>();
private List<String> topics = new ArrayList<String>();
public JsonResponse(){
}
public List<Integer> getPercentages() {
return percentages;
}
public void setPercentages(List<Integer> percentages) {
this.percentages = percentages;
}
public List<String> getTopics() {
return topics;
}
public void setTopics(List<String> topics) {
this.topics = topics;
}
}
Then Ive got a#Component` annotated class which contains a method that returns a model object (of the class that I wrote above), with the 2 arraylists attributes populated.
#Component
public class ChartUtils {
#Autowired
public JsonResponse response;
public JsonResponse listPieChartData( ModelAndView model ,int waveId ){
//arraylists for chart generation
List<Integer> percentages = new ArrayList<Integer>();
List<String> topics = new ArrayList<String>();
{... code for accessing the DB and processing some data and then populating the 2
arraylists ... }
response.setTopics(topics);
response.setPercentages(percentages);
return response;}
}
So the Controller class, the one that has the mapping for the action that I am calling to gather data for the chart generation and in which I am calling listPieChartData method, from the class above, and in which I'm also using the #ResponseBody annotation is this:
#Controller
public class ChartController {
#Autowired
public ChartUtils utils;
#Autowired
public JsonResponse response;
#RequestMapping(value = "/drawPieChart", method = RequestMethod.GET )
#ResponseBody
public JsonResponse drawPieChart( ModelAndView model,
#RequestParam(value = "id", defaultValue = "-1") int waveId ) {
return utils.listPieChartData(model,waveId ); }
The JavaScript function that draws the chart :
function drawColumnChart(percentages, topics , div,width,height) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Wave');
for (var i=0; i < topics.length; i++){
data.addColumn( 'number', topics[i] );
}
data.addRow( percentages );
var wave=percentages[0];
var options = {
'title':'Generated Chart For '+wave,
'backgroundColor': { fill: "none" },
'is3D': true,
'width':width,
'height':height,
};
var chart = new google.visualization.ColumnChart(document.getElementById(div));
chart.draw(data, options);
}
And the AJAX call to the controller's mapped method (for gathering data) that finally calls the above JS function to obtain the chart (I'm also sending the request param int id for the controller method , I didn't wrote that)
$("#button").live("click", function(){
var arrayP, arrayT;
$.ajax({
url: "drawPieChart",
contentType: "application/json",
data: params,
success: function(data) {
$.each(data, function(messageIndex, message) {
if (messageIndex === 0) {
arrayP = message;
} else {
arrayT = message;
}
});
drawPieChart(arrayP, arrayT,'chart_div',600,400);
}
});
});
I know this is a lot of code :) but it's pretty simple code, to understand the flow better, here is how it`s working:
From a button input I'm calling, with AJAX, the mapped method to the drawPieChart action (which is in the ChartController class), this methods sends the response through invoking the listPieChart method (from the ChartUtils class), which returns a JsonResponse object, (which contains 2 arraylists). This JsonResponse should be converted to a JSON, because in the AJAX request, I'm telling that the request needs a JSON input (via contentType: "application/json"), and it should get it because I use #ResponseBody in the controller method mapped for this request.
I`m getting this response:
The resource identified by this request is only capable of generating
responses with characteristics not acceptable according to the request
"accept" headers ().
(HTTP Status 406)
Please correct me where I'm wrong, I just can't get this working and I can't figure out why...
And my servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up
static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.bla.bla" />
<beans:import resource="classpath:springJDBC.xml" />
</beans:beans>
So the problem was that i didn'd have all the Jackson dependencies declared in pom.xml.
These are the dependencies for your maven project in case you want Spring 3 to automatically serialize an object for you , using the #ResponseBody annotation , as a response from a method.
Noob stuff , but I didn't saw this mentioned in the examples that I found .
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>
Also , I had to change some stuff in the ajax call for invoking the method that is returning the json object with data for the chart generation :
$("#buttonPieGenerate").live("click", function(){
$.ajax({
url: "drawPieChart", //method from controller
contentType: "application/json",
data: params,
success: function(data) {
drawPieChart(data.percentages, data.topics,'chart_div',600,400);
}
});
});
I'm accessing the data in the Json object that I`m getting as a response from the call with data.percentages , data.topics .
A small update for the world of 2015:
<dependency>
<!-- Just placing this on the classpath will enable JSON for #ResponseBody -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>