I want to pass data from one application (Spring MVC) to another application written with ZK Framework using POST request.
One of the parameters of the post method will be a file. Let's call it id.
So, what I do have now is Composer with the following code:
public class PictureComposer extends SelectorComposer<Component> {
#Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
Execution e = Executions.getCurrent();
System.out.println("post param: " + Executions.getCurrent().getParameterMap().get("id"));
HttpServletRequest request = (HttpServletRequest) e.getNativeRequest();
String idd = request.getParameter("id");
System.out.println("id: " + idd);
}
}
And, when I try to make a POST request to this composer, Executions.getCurrent().getParameterMap().get("id") seems to return null.
Same thing with the second approach (using HttpServletRequest).
On the other hand, I get some value for id, when I use GET request and pass this value as a parameter in the URL. But, unfortunately, I cannot pass my file in the URL.
So, the main question is how I can retrieve the variable from the POST request?
UPD: (for now I try to retrieve a plain string parameter, just as simplified example) My POST request looks like this:
POST /zssessentials/picture.zul HTTP/1.1
Host: localhost:8080
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="id"
wooooow
----WebKitFormBoundaryE19zNvXGzXaLvS5C
In order to retrieve an uploaded file, you should set enctype property of your <form> element to multipart/form-data, as below:
<form method="POST" action="your/zk/address" enctype="multipart/form-data" >
...
</form>
Then you can use the getPart method of HttpServletRequest class to access the uploaded file. So your code should be changed to something like this:
Part filePart = request.getPart("id");
InputStream filecontent = filePart.getInputStream();
I've figured out.
The reason was that the servlet didn't parse the multipart requests on its own.
I had been sending requests with this line in the header (even if it was plain string or integer values):
Content-type: multipart/form-data
and that was the reason.
So, I was expected to use the third-party library in the PictureComposer class.
In my case Streaming API from Apache came in handy.
Also, this StackOverflow answer helped me a lot in resolving my problem.
Related
I'm trying to download an Excel file using RESTful call. I thought it should be pretty simple, but I keep getting HTTP 406 Not Applicable Eror. This is what my controller method looks like:
#RequestMapping(value = "/gettemplate", method = RequestMethod.GET, produces = "application/vnd.ms-excel")
#ResponseBody
public Response getExcelTemplate() throws Exception {
File templateFile = new File("TestFile.xlsx");
ResponseBuilder builder = Response.ok(templateFile,"application/vnd.ms-excel");
builder.header("Content-Disposition", "attachment; filename=\"" + templateFile.getName() + "\"" );
return builder.build();
}
I've tried setting request header to accept application/vnd.ms-excel, I've also tried using application/octet-stream instead of vnd.ms-excel. I get an HTML response back with 406 error message in either case. Here's what my Ajax test call looks like:
Ext.Ajax.request({
url: 'myservice/gettemplate',
//dataType: 'application/vnd.ms-excel',
headers: {
'Accept': 'application/vnd.ms-excel, text/plain, */*'
//'Content-Type': 'application/vnd.ms-excel'
},
success: function (response, options) {
alert(1);
},
failure: function (response, options) {
alert(2);
}
});
I've commented out the lines that I've tried and removed as it didn't help. This could be a very simple config change, but I can't seem to figure out.
When you annotate a request mapping return type with #ResponseBody, Spring will attempt to convert the object into the response using an HttpMessageConverter, as it says on the reference page:
#ResponseBody As with #RequestBody, Spring converts the returned object to a
response body by using an HttpMessageConverter.
You can see the list of available converters here: Message converters
It looks like the application/vnd.ms-excel you specify is not supported by any of the converters. Maybe that is why you get a 406.
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent
in the request.
The solution in your case is to remove the #ResponseBody annotation and handle the file download in another way.
See here for examples on how to download a file from a Spring controller:
Downloading a file from Spring controllers
Returning a file from a controller in Spring
Somewhat based on ESala's answer I tried a few things. Finally got it working by setting header and content type to HttpServletResponse. Here's the code that worked for me. This works in both IE and Firefox flawlessly with just a simple anchor tag, without even an ajax call. Hope it helps someone else.
#RequestMapping(value ="/gettemplate", method = RequestMethod.GET)
#ResponseBody
public void getExcelTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception{
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-disposition", "attachment; filename=TestFile.xlsx");
InputStream fis = getClass().getClassLoader().getResourceAsStream("templates/TestFile.xlsx");
int x = fis.available();
byte byteArray[] = new byte[x];
logger.info(" File size :"+byteArray.length);
fis.read(byteArray);
response.getOutputStream().write(byteArray);
response.flushBuffer();
fis.close();
}
I know how to create endpoints that are handling files using MediaType.MULTIPART_FORM_DATA and #FormDataParam("file") FormDataBodyPart bodyPart, but I was wondering if I can also have JSON data along that request? Something like:
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#RequestBody SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
return null;
}
At the moment if I add some JSON data on the "raw" tab on the following Postman request I'm getting HTTP 415 Unsupported Media Type probably because I specified that I consume MULTIPART_FORM_DATA but I'm also using #RequestBody which is looking for JSON content which is APPLICATION_JSON. So how can I have JSON data and a file handled in the same request? I know that it's possible to do that in two requests, I just want to do it in one if possible?
Why are you using both Spring and Jersey annotations? You should stick to using the annotations meant for the framework. Since you are using Jersey, should stick to its its annotations.
So here are the things to consider about your current code and environment.
There can't be two separate bodies. With your code, that's what it appears you expect to happen.
You can though put the JSON as part of the multi-part body. For that you should also annotate the SomeModel with the Jersey #FormDataParam
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(
#FormDataParam("model") SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
}
In the Jersey configuration, you need to make sure to register the MultiPartFeature. If you don't the body won't be able to be deserialized, and you will get exceptions and error responses.
Now the Postman problem. You can see similar problem here. The problem was that the Content-Type was not set for the JSON body part. For example the body might look something like
--AaB03x
Content-Disposition: form-data; name="model"
{"some":"model", "data":"blah"}
--AaB03x
Content-Disposition: form-data; name="file"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
You can actually see the body, if you hit the Preview button in Postman. The problem is that there is no Content-Type for the "model" part, as you can see in the "file" part. This happens because you can't set individual parts' Content-Type in Postman. The one that you will see will be discovered from the file extension. For example a .txt file will make Postman set the Content-Type to text/plain and a .png file to image/png.
If you look in the link above, I proposed maybe you could use a .json file instead of typing in the data. Of course that was just a theory. I didn't actually test it.
In any case, the Content-Type must be set in order for Jersey to be able to know to deserialize it as JSON. If the .json file extension theory doesn't pan out, then you can use a different client, like cURL, which I showed an example in the link, or you can use the Jersey client to test, as seen here.
Don't set the Content-Type header to multipart/form-data in Postman. It sets it for you when you use the form-data. I just saw a post where someone said there is bug when you set the header. Can't find the post now, and not something I've confirmed, but I'd just leave it out.
UPDATE
So the OP was able to find a way to set the Content-Type: application/json to the "model" part. But it is sometimes the case where with a Javascript client, you are not able to set it. So there will be no Content-Type. If this is the case, Jersey will not be able to deserialize the JSON, as it has no idea that it is actually JSON being sent. If you absolutely can't or have no idea how to set the Content-Type for individual parts, you could resort to doing the following.
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#FormDataParam("model") FormDataBodyPart jsonPart,
#FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
SomeModel model = jsonPart.getValueAs(SomeModel.class);
}
Yes, you can get that as multipart form data.
you get like this in angularjs:
$scope.uploadFile = function () {
var file = $scope.selectedFile[0];
$scope.upload = $upload.upload({
url: 'api/upload',
method: 'POST',
data: angular.toJson($scope.model),
file: file
}).progress(function (evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
}).success(function (data) {
//do something
});
};
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
public Response uploadFileAndJSON(#RequestParam("data") String data,
#MultiPartFile("file")File file) {
you can data as form data and convert it
like you want to your object using Gson jar.
return null;
}
Have a look at it for angularjs code:
Angularjs how to upload multipart form data and a file?
https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/
I'm working on a REST resource that takes in a key and data (value) . I'd like to be robust so this data can be anything. It could range from a string to a file.
To handle this, I have the PUT REST method which is at the end of the post. Nothing fancy, just trying to PUT basic data. If I send data that is a string it works no problem.
However, if I try to send a file across, I get this error: org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type. Here's why this doesn't make sense to me:
If I change the #Consumes from application/octet-stream to #Consumes("multipart/form-data"), I can send a file across just fine. When examining the file that's stored I see something like this:
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Disposition: form-data; name="fileUpload1"; filename="uploadedFile.extension"
Content-Type: application/octet-stream
/* file information here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
Notice that the Content-Type is supposedly application/octet-stream which is what I tried consuming before but got the Cannot consume content type exception. I have no idea what would be causing this. I'm using the Advanced Rest Client extension for Chrome to send my request and it looks something like this: https://i.imgur.com/KvKCIkl.jpg
Any thoughts?
#PUT
#Path("{key}")
#Consumes("application/octet-stream")
public Response addData(
final #PathParam("key") String key,
final InputStream data) {
final Service service = new Service();
try {
service.addData(key, data);
} finally {
IOUtils.closeQuietly(data
}
return Response.status(204).build();
}
A multipart/form-data message contains a series of parts divided by a boundary. Every part has its own Content-Type. So in your example Content-Type: application/octet-stream is the Content-Type of one (and probably the only one) part but the Content-Type of the whole message is multipart/form-data. A full message with multiple parts could look like this:
Content-Type: multipart/form-data; boundary=WebKitFormBoundaryfuQalizBHtg1BiLJ
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Disposition: form-data; name="fileUpload1"; filename="uploadedFile.extension"
Content-Type: application/octet-stream
/* file information here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Type: text/plain
/* some text here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
i know you shouldn't send a HTTP GET Request with a body, but ceilometer web api forces me to do so.
I'm developing a ceilometer scala client, so I need a scala/java way to make a get request with a body.
So far I tried with beeClient (http://www.bigbeeconsultants.co.uk) and in plain Java using httpConnection but I get a 404 error.
In curl I can achieve the result in this way:
curl -X GET -H "X-Auth-Token: ..long long token here.."
-H "Content-Type: application/json"
-d '{"q": [{"field": "resource", "op": "eq", "value": "gdfsf"}]}'
http://137.204.57.150:8777/v2/meters/
That's my scala code that uses java HttpURLConnection:
import java.io._
import java.net._
val token = "myToken"
val url = new URL("http://137.204.57.150:8777/v2/meters/")
val body = "{\"q\": [{\"field\": \"resource\", \"op\": \"eq\", \"value\": \"gdfsf\"}]}"
val bodyLenght = body.length.toString
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
connection.setRequestProperty("Content-Type", "application/json")
connection.setRequestProperty("Content-Length", bodyLength)
connection.setRequestProperty("Accept", "*/*")
connection.setRequestProperty("X-Auth-Token", token)
connection.setDoInput(true)
connection.setDoOutput(true)
//SEND REQUEST
val wr = new DataOutputStream(connection.getOutputStream)
wr.write(body.getBytes)
wr.flush
wr.close
if (connection.getResponseCode == 200)
println("ok")
else
println("error")
What's the difference between my Java implementation and the curl command? I can't see any, I tried checking the header of curl calling it with the -v argument and that's what I get:
* Hostname was NOT found in DNS cache
* Trying 137.204.57.150...
* Connected to 137.204.57.150 (137.204.57.150) port 8777 (#0)
> GET /v2/meters/ HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 137.204.57.150:8777
> Accept: */*
> X-Auth-Token: ...Token....
> Content-Type: application/json
> Content-Length: 60
>
* upload completely sent off: 60 out of 60 bytes
* HTTP 1.0, assume close after body
And then I get the response.
Thank you in advance
I resolved the problem using jetty-client implementation, that lets build http requests in anyway you want. Here's the code (it's not immutable but it's not that bad in scala):
val httpClient = new HttpClient()
httpClient.setConnectTimeout(connectTimeout)
httpClient.setFollowRedirects(false)
httpClient.setStopTimeout(readTimeout)
httpClient.start()
val resp = httpClient.newRequest(uri).
method(HttpMethod.GET).
header("X-Auth-Token",s).
send()
Look that i'm using the blocking API but jetty provides also a Java NIO API.
I found a working plain java solution here, using apache's httpclient, httpcore, and commons-logging libs.
You need to create a class and extend HttpEntityEnclosingRequestBase, overriding the method name:
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
public final static String METHOD_NAME = "GET";
#Override
public String getMethod() {
return METHOD_NAME;
}
}
Then you just use it like this:
HttpClient httpClient = HttpClientBuilder.create().build();
HttpGetWithEntity e = new HttpGetWithEntity();
e.setURI(new URI(yourURL))
e.setEntity(yourEntity);
HttpResponse response = httpclient.execute(e);
Hope it helps.
In general, the specification does not prohibit body on any type of http request (GET, DELETE etc), so you can do it if needed. However by convention this is atypical.
The problem you're having is that there are assumptions about what you can and can't do in the implementation of URLConnection you're using. In general, you'll be using a HttpUrlConnection (as you cast to), which will actually be implemented by your jvm. For example, here is a sun specific implementation.
If you look at this implementation, you will see it assumes that a GET request where you need the output stream is actually a POST.
If you want a GET with a body, you need to use a different connection method, for example a library like apache-http-client. You could start by looking at this question. There may be better scala alternatives for you to start with.
You use HTTP PUT or POST request when sending request body for Celiometer API.
I checked the Ceilometer documentation and found that all requests with request body use HTTP PUT or POST methods. No GET method with request body.
http://docs.openstack.org/developer/ceilometer/webapi/v2.html
After checking the documentation of Ceilometer and cURL I can suggest two things.
Use URL parameters instead of JSON
As per the documentation you can use the URL parameters or JSON. You can modify your request as specified below to achieve the same thing with URL parameters rather than JSON.
URL("http://YOURHOST.COM:8777/v2/meters/?q.field=resource&q.op=eq&q.value=gdfsf")
In case you have a specific reason not to use URL parameters for your JSON approach I guess encoding is what is missing in your request. Parameters are required to be sent in query parameters only rather than body content. For that I guess you need to try with below encoded data as shown below based on your request in the question.
URL("http://YOURHOST.COM:8777/v2/meters/?q=%5B%7B%22field%22%3A%20%22resource%22%2C%20%22op%22%3A%20%22eq%22%2C%20%22value%22%3A%20%22gdfsf%22%7D%5D%7D")
Here q is the root query parameter name, without token I was not able to validate it.
Replace YOURHOST.COM with ip address for your server as it was showing problem to me even after putting them in code block and please let me know.
you can try like this also
#RequestMapping(value = "/listcategories", method = RequestMethod.GET)
private ModelAndView getCategories() {
ModelAndView modelAndView = new ModelAndView("list-of-categories");
List<Category> categories = categoryService.getAllCategories();
modelAndView.addObject("categories", categories);
return modelAndView;
}
I was reading the files_put documentation for the Dropbox API.
The URL Path they use is: https://api-content.dropbox.com/1/files_put/<root>/<path>?param=val and request body holds the file:
required The file contents to be uploaded. Since the entire PUT body
will be treated as the file, any parameters must be passed as part of
the request URL. The request URL should be signed just as you would
sign any other OAuth request URL.
Questions
I am curious to know what is the content-type of this type of request? (file in request body and parameters in url string)
How can this API functionality be mimics? specifically in a grails controller. Something like this.
How would this type of request be tested in cURL Update : I found out how to test this with curl here.
For the controller I envisioned something like this
def save () {
withFormt {
html {actForHTML}
<something> {actForREST}
}
}
def actForREST () {
//how can I get access to the file? I guess url parameters can be accessed by `params`
}
REST console does not have the ability to send binary data in request body. Unfortunately, I cannot access curl right now. But I have few inputs for you, and I am also going to try the same in my personal machine.
How to use curl for file upload? (#source - cURL docs)
4.3 File Upload POST
Back in late 1995 they defined an additional way to post data over HTTP. It
is documented in the RFC 1867, why this method sometimes is referred to as
RFC1867-posting.
This method is mainly designed to better support file uploads. A form that
allows a user to upload a file could be written like this in HTML:
<form method="POST" enctype='multipart/form-data' action="upload.cgi">
<input type=file name=upload>
<input type=submit name=press value="OK">
</form>
This clearly shows that the Content-Type about to be sent is
multipart/form-data.
To post to a form like this with curl, you enter a command line like:
curl --form upload=#localfilename --form press=OK [URL]
W3C Specification
Have a look at the W3C Spec here and the RFC1867 for multipat/form-data
Grails Controller to handle request
Your app should be able to handle the multipart/form-data(no MIME type addition should be required, I think). Your action in the controller should look like below:-
For example:
def uploadFileAndGetParams(){
def inputStream = request.getInputStream()
byte[] buf = new byte[request.getHeaders().CONTENT_LENGTH] //Assuming
//Read the input stream
for (int chunk = inputStream.read(buf); chunk != -1; chunk = is.read(buf)){
//Write it any output stream
//Can refer the content-type of the file (following W3C spec)
//and create an Output stream accordingly
}
//Get the params as well
//params.foo //params.bar
}
It may not be full proof but it should be less complicated than what I thought it would be. I am going to try the same today. Useful post to look at.