How to Handle the response from server after a form submission? - java

I'm attempting to create ajax functionality to a form that uploads files. I created the isolated iframe and target the form submission to it. The server validates the files and sends back an XML doc in the response body. How would I handle and retrieve the XML doc on the client side, preferably using JavaScript?
This is my handler,
#Controller
#RequestMapping("/upload")
public class UploadController {
#RequestMapping(method = RequestMethod.POST)
public String handleFileUpload(HttpServletResponse response, #RequestParam("file") MultipartFile file) throws IOException{
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write("<test>hello</test>");
return null;
}
}

We do something similar but return JSON instead of XML. That JSON is then used as-is by the JavaScript function that triggers the upload ----
If i use the response type as json in the iframe form submit for file upload..i am seeing a download popup asking me to save or open... the application/json response is handled by the browser as a download ... issue occurs in IE and older versions of FF

There were several suggestions on using a hidden iframe on the net. That's what my final attempt was, I sent an xml doc to the iframe's body and manage the data from there.

Related

Spring - from JSON response to webPage

I have built a simple service in Spring:
#RequestMapping(value = "/list", method = RequestMethod.GET)
public #ResponseBody List<Person> eventList() throws ParseException {
return personDAOImpl.list();
}
When I invoke the corresponding URL, I see a JsonObject list like this:
[{"id":2,"name":"John"},{"id":3,"name":"Jack"},{"id":4,"name":"Sam"}]
This service will be invoked both from a mobile app (and I know how to use a Json response in this case) and from a browser, and so I want that the /list service returns a Json.
By now, if I write the url in a browser (typing localhost:8080/myproject/eventList) I simply obtain the aforementioned Json String displayed on the screen.
How could I obtain a user-friendly interface that shows to the user a more friendly, like if I used a jsp?
ps: I have already used a service that successfully returns a jsp page (using ModelAndView), and that shows a table with the items, but I repeat that I would like to get a json response and than parsing it in a different way depending from the client (web browser or mobile app).
Thank you in advance!
You could build a client app which makes requests to your API using JavaScript.
For example,
localhost:8080/jsp/home returns a jsp page Home.
The Home page makes AJAX request (using JavaScript) to localhost:8080/myproject/eventList and recieves the JSON object. jQuery.get()
The Home page adds (using JavaScript) some visual elements to html. jQuery.append()
That's all.

Download an XML file using a REST api

I need to write a REST api which would send an XML file as response.
I am confused with what MediaType should be defined as the response.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
or
#Produces(MediaType.APPLICATION_XML)
The API will be served from a GET request and I want the file to be downloaded in the client side.
I will go with #Produces(MediaType.APPLICATION_XML), as the #Produces(MediaType.APPLICATION_OCTET_STREAM) is a constant for application/octet-stream which is usually used for binary type (such as video, music and stuff)
I suggest dont send file directly.
read file at server end and send the file content as a response.
so it will look like
#GET
#Produce("application/xml")
public Response sendData(){
//code to read file and store in a string object
return Response.built.ok(string object);
}

Spring: JSON data and file in the same request

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/

Struts 1 Action Return Success After Downloading a Binary File

I know how to download a binary file from my web app by setting the response header and copying the binary file to the response's outputstream. But what I'm having trouble with is returning success so the page will reload. If I return success I will get the error:
java.lang.IllegalStateException: getOutputStream() has already been
called for this response
See the below code example. This will download the file and then throw the exception. Is there a way to restore the response?
public ActionForward export(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
//tell browser program going to return an application file
//instead of html page
response.setContentType("application/force-download");
response.setHeader("Content-Disposition","attachment;filename=temp.csv");
IOUtils.copy(new FileInputStream("/path to some file"), response.getOutputStream());
response.flushBuffer();
return mapping.findForward("success");
}
I don't believe you can do a redirect or reload after a file download. This is more of an HTTP restriction, not something specific to Struts 1.
It takes one HTTP response to download a file to a browser, and one HTTP response to reload the page. You are attempting to do both from the same HTTP request, which simply isn't possible. A request cannot have more than one response.
In much the same way, you can't issue a redirect after you've served a page to the user, unless the page itself contains a <meta refresh="..."> element or some JavaScript that does a reload. Both approaches essentially create another HTTP request, but neither approach is open to you because it's not possible to do either with a file download.
In short, it's not possible to do what you are asking for.
You can set response.setHeader("Refresh", "1"); according to this article:
http://users.polytech.unice.fr/~buffa/cours/internet/POLYS/servlets/Servlet-Tutorial-Response-Headers.html
But it doesn't work when you close browser file download popup.

Cannot Compress Java Server Response Sent To JSP

I am having trouble in returning compressed response (GZip) from my Java Servlet, to a JSP.
Flow :
Request comes to java servlet
Process the request, and create a JSON object, with the response
Convert the JSON object to string
Compress the response string with GZip
The compressed response string is set as attribute in the request object and control passed to JSP
In the JSP, the response string (compressed) is printed on screen
Precautions :
Request object has "Accepting-Encoding" set with "gzip"
Response header has "Content-Encoding" set to "gzip"
Response content type is set to "application/json"
Response character encoding is set to "ISO-8859-1"
Result :
Firefox shows "Content Encoding Error"
Chrome shows "Error 330 (net::ERR_CONTENT_DECODING_FAILED): Unknown error."
Can anyone help point me out, in the right direction please?
The compressed response string is set as attribute in the request object and control passed to JSP
You shouldn't have forwarded a JSON response to a JSP. You should have printed the JSON plain to the response and have the JavaScript/Ajax code in your JSP Android app to call the URL of the servlet which returns the JSON. See also How to use Servlets and Ajax?.
As to the GZIP compression, you shouldn't do it yourself. Let the server do itself.
Fix your code to remove all manual attempts to compress the response, it should end up to basically look like this:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String json = createItSomehow();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
That's all, if you let your Android app call the URL of the servlet, it'll retrieve the JSON string.
Finally edit the server configuration to turn on automatic GZIP compression. In case of for example Tomcat, that would be a matter of adding compression="on" to the <Connector> element in Tomcat's /conf/server.xml file:
<Connector ... compression="on">
As per the documentation, the compressable mime types defaults to text/html,text/xml,text/plain. You can configure this to add application/json.
<Connector ... compression="on" compressableMimeType="text/html,text/xml,text/plain,application/json">
Unrelated to the concrete problem, the response character encoding must be set to UTF-8 which is as per the JSON specification.
JSPs are for rendering textual data to the client. GZIP is binary data, even if it is compressed text underneath.
I suggest using a GZIP servlet filter to compress your data on the fly, instead of doing it programmatically in your business logic.
See this prior question for how to get hold of one off-the shelf: Which compression (is GZIP the most popular) servlet filter would you suggest?
Failing that, then write your own servlet filter that does the same thing.

Categories

Resources