I have a Java REST endpoint which should return a multipart response with a json document and a pdf file. I chose "form-data" and not "mixed" because I want to assign a name to the parts in case I have multiple different json or file parts in the future.
The problem: In the output, the parts seem to be sorted depending on the key I specify, which makes sense, since formDataMap of MultipartFormDataOutput is a HashMap. If the json part is first, everything works as it should. BUT if the file part is sorted first, then the json part is missing in the output, as well as the form-data boundary at the end.
I feel I've tried every possible variation by switching out media types, response types, annotations, trying different pdf files, etc but nothing seems to help.
Does anyone have an idea how to explain this behavior? Any help is welcome, thanks!
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
#Produces("multipart/form-data")
public Response test(MyObject o)
{
// process input and get the byte arrays for the json document and pdf file
var output = new MultipartFormDataOutput();
output.addFormData("foo.json", new ByteArrayInputStream(myJsonByteArray), MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8"), "foo.json");
output.addFormData("document.pdf", new ByteArrayInputStream(myPdfByteArray), MediaType.valueOf("application/pdf"), "document.pdf");
return Response.ok(output).build();
}
I'm on Quarkus 1.13.7 with Resteasy 4.5.12
Not sure if this helps, but try wrapping MultipartFormDataOutput with GenericEntity before returning.
GenericEntity<MultipartFormDataOutput> outputGen = new GenericEntity<MultipartFormDataOutput>(output) {};
return Response.ok(outputGen).build();
Related
Can we provide both Json response as well as a csv file as attachment in a Rest Service?
I have written a code like below, but I am also sure its not going to work.
ResponseBuilder responseBuilder = null;
responseBuilder = Response.status(200).type(MediaType.APPLICATION_JSON)
.entity(parseOrganizations(getOrganizationsResponseMashery(limit, offset)));
responseBuilder.type(MediaType.TEXT_PLAIN).entity(file).header("Content-Disposition", "attachment; filename=Organizations.csv");
return responseBuilder.build();
The second setter for entity with file, basically over writes the json content that I had inserted earlier as entity. So please suggest.
Yes, that's right, an HTTP response should be of a single type. If you are telling you return JSON, then the client will be expecting a JSON object, not a file. And similarly, if you say you return a file, it will be expecting a file.
The client will be taking an action based on the return type stated in the response headers (Eg: Mapping a JSON object to a class instance, etc.), so it is important this is unambiguous.
In the case of springboot, it appears the last call to the type method overwrites an previous one.
I am working on a Java PDF Generation micro service using spring boot. The pdf generation is meant to be a 2 stage process.
Templating - html template with some sort of expression language, which reads a json structure directly
HTML to PDF - this generates a pdf from the html produced in step 1
Note: I have some java and JavaScript (nunjucks/nodejs) solution for steps 1 and 2, but I really need a more maintainable approach as follows.
My restful endpoint will take 2 parameters an html template and a json
The json structure maps to the html template one to one and both files have been predefined to strict contract.
The service endpoint should not do any object mapping of json data to html Dom elements e.g. table, rows, etc
The endpoint only embeds the json data to the html using logic in java code
The html gets executed/rendered and reads the json directly using some sort of expression language, since it contains the json structure
The endpoint then response with an html with the dynamic data, which can be sent to the pdf generator web service endpoint
Below is a sample code:
#POST
#Path("createPdf")
public Response createPdf(String htmlTemplateParam, String json) {
//Read template from endpoint param or start off with reading local html template from resource folder
String templateHtml = IOUtils.toString(getClass().getResourceAsStream(HTMLTemplateFiles.INCOMING_TEMPLATE));
RequestJson requestJson = [Prepare json before passing to HTML Template]];
if(requestJson.isContentValid()) {
LOG.info("incoming data successfully validated");
// TODO
// Pass the requestJson (Endpoint Param JSON ) to templateHtml
// Trigger the reading of the Json data and populating different HTML DOM elements using some sort of expression predefined in HTML
// Get hold of the rendered HTML
String resolvedHtml = [HTML with data from json param into endpoint];
// The next bit is done
String pdf = htmlToPdfaHandler.generatePdfFromHtml(resolvedHtml);
javax.ws.rs.core.Response response = Response.ok().entity(Base64.decodeBase64(pdf)).build();
}
}
The templating stage one is where I need help.
Please, what is the best technical solution for this?
I am comfortable with Java and JavaScript framework and happy to learn any framework you suggest.
But, my main design goal is to ensure that as we have new templates and a template and data changes, a non-techie can change html/json and generate pdf.
Also, no java code changes should be required for template and data changes.
There are a few things in my head like jsonpath, thyme leaf, JavaScript, etc But, I love best practice and like to learn from someone with the real-life experience of similar use case.
After further research and first answer I am also thinking of the freemarker solution below.
But, how would I create a free marker template-data auto-magically from reading input json.i.e. without creating POJO/DTO?
Based on first answer :
Configuration cfg = new Configuration(new Version("2.3.23"));
cfg.setDefaultEncoding("UTF-8");
// Loading you HTML template (via file or input stream):
Template template = cfg.getTemplate("template.html");
// Will this suffice for all JSON Structure, including nested deep ones
Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, String[]> templateData = new Gson().fromJson(json, mapType);
try (StringWriter out = new StringWriter()) {
// In output stream the result will be template with values from map:
template.process(templateData, out);
System.out.println(out.getBuffer().toString());
out.flush();
}
Thanks in advance.
NOTE: Code snippets, pseudo code, references are welcomed.
One of the options might be FreeMarker usage:
Configuration cfg = new Configuration(new Version("2.3.23"));
cfg.setDefaultEncoding("UTF-8");
// Loading you HTML template (via file or input stream):
Template template = cfg.getTemplate("template.html");
// You need convert json to map of parameters (key-value):
Map<String, Object> templateData = new HashMap<>();
templateData.put("msg", "Today is a beautiful day");
try (StringWriter out = new StringWriter()) {
// In output stream the result will be template with values from map:
template.process(templateData, out);
System.out.println(out.getBuffer().toString());
out.flush();
}
I want to save the entities in our program into .json files to get a better connection between backend and our Angular frontend. For this, I wrote some tests and during the execution, the structure is saved in the files as desired.
The structure is sampled by
ObjectMapper objectMapper = new ObjectMapper();
try{
ObjectWriter writer = objectMapper.wrtier(new DefaultPrettyPrinter());
String result = objectMapper.writerWirthDefaultPrettyPrinter().writeValueAsString(new OurObject());
writer.writerValue(new File("path"), result);
}
What I got
"{\r\n \"firstProp\": something,\r\n \"secondProp\": anything,\r\n...
But I want, that the file contains the classical JSON structure to make it better readable, this means:
{
"firstProp": something,
"secondProp": anything,
...
What can I do, to write it in the desired JSON structure?
Thanks for any help
Matthias
You're double-encoding the json string
Remove writeValueAsString and try to directly use writer.writerValue(file, object)
But if you're emitting this from a Java backend, it's typically best practice to serve it from an HTTP request, not as a file to any front-end
I have a JSON that looks more or less like this:
{"id":"id","date":"date","csvdata":"csvdata".....}
where csvdata property is a big amount of data in JSON format too.
I was trying to POST this JSON using AJAX in Play! Framework 1.4.x so I sended just like that, but when I receive the data in the server side, the csvdata looks like [object Object] and stores it in my db.
My first thought to solve this was to send the csvdata json in string format to store it like a longtext, but when I try to do this, my request fails with the following error:
413 (Request Entity Too Large)
And Play's console show me this message:
Number of request parameters 3623 is higher than maximum of 1000, aborting. Can be configured using 'http.maxParams'
I also tried to add http.maxParams=5000 in application.conf but the only result is that Play's console says nothing and in my database this field is stored as null.
Can anyone help me, or maybe suggest another solution to my problem?
Thanks you so much in advance.
Is it possible that you sent "csvdata" as an array, not a string? Each element in the array would be a separate parameter. I have sent 100KB strings using AJAX and not run into the http.maxParams limit. You can check the contents of the request body using your browser's developer tools.
If your csvdata originates as a file on the client's machine, then the easiest way to send it is as a File. Your controller action would look like:
public static void upload(String id, Date date, File csv) {
...
}
When Play! binds a parameter to the File type, it writes the contents of the parameter to a temporary file which you can read in. (This avoids running out of memory if a large file is uploaded.) The File parameter type was designed for a normal form submit, but I have used it in AJAX when the browser supported some HTML5 features (File API and Form Data).
I'm working on a chat application, and I need to process a get request for a file that has been uploaded to the database. I'm not sure if I should return an output stream or a file or what.
The idea is that it will be something like any other chat application where the image appears as message are loaded. Using an output stream seemed like the best option, but I wasn't sure how to create the output stream from the information in the database, which includes an id, checksum, name, size, and mime type.
So my questions are:
How should I approach this?
if output stream is the best way, what's the ideal way to implement it?
Any guidance is appreciated, please let me know if I can make the question more clear, or if more details are necessary to answer the question.
What I couldn't understand how to do is this: serve the image to the front-end/client code. As it turns out, it was super easy.
#GET #javax.ws.rs.Path("/file/{fileId}")
public Response getFile(#Context SecurityContext sc, #PathParam("id") long topicId, #PathParam("fileId") long fileId) {
TopicFile tFile = topicAccessor.getFile(fileId);
String fileLocation = "/server/uploads/" + tFile.getChecksum();
File file = new File(fileLocation);
return Response.ok(file, tFile.getType()).build();
}
Here TopicFile holds metadata for the file in the database, and the files are named their checksum.
So basically the solution to my problem was to return a Response. I hadn't thought of this earlier because I "inherited" this code, and I trusted that the previous person had god reason not to use the Response class.