It seems somewhat awkward whenever I need to deal with custom responses data type on Spring MVC.
In my case, I need to return a JavaScript content. However, I want it to be cache.
So to clarify, this is not a static file case (<mvc:resources...), Instead it's a dynamic file generated on the server, that I do want to cache (i.e. HttpResponse 200 and HttpResponse 302).
In terms of code, on the client side I simply have:
<script src="<spring:url value='/some-file.js'/>"></script>
Than a SpringMVC controller:
#RequestMapping(value = "/some-file.js")
public ResponseEntity<String> resourceBundles(HttpServletRequest request, HttpServletResponse response, Locale locale) throws IOException {
responseHeaders.add("Cache-Control", "public, max-age");
responseHeaders.add("Content-Type", "text/javascript; charset=UTF-8");
responseHeaders.add("Expires", "max-age");
// Turn this into JSON response:
String someJson = "{ a:a, b;b};";
return new ResponseEntity<String>("var data = " + someJson, responseHeaders, HttpStatus.OK);
}
However, it seems that the browser is always trying to access this dynamic JS file.
Since this file is Session depended, I can not generate it and treat it as a static file.
Any suggestions?
This is the correct behavior. Most browsers will send you a GET request with a If-Modified-Since + time stamp to check whether a file has changed.
In the usual case, you'd use the time stamp to figure out whether the file has changed. But since it never changes in your case, you can reply with a 304 / HttpStatus.NOT_MODIFIED response without a response body (instead of 200 / OK).
That tells the browser that the file hasn't changed.
This should work:
#RequestMapping(value = "/some-file.js")
public ResponseEntity<String> resourceBundles(
HttpServletRequest request,
HttpServletResponse response, Locale locale) throws IOException {
Date lmod = session.getAttribute("lmod");
if( null == lmod ) {
lmod = new Date();
session.setAttribute("lmod", lmod);
}
responseHeaders.add("Last-Modified", lmod);
String ifModifiedSince = request.getHeader("If-Modified-Since");
if( null != ifModifiedSince ) { // You may want to compare lmod and ifModifiedSince here, too
return new ResponseEntity( responseHeaders, HttpStatus.NOT_MODIFIED );
}
... create first time response ...
}
Telling the browser Last-Modified will enable it to send you If-Modified-Since
Related
I am trying to useDOLBY.IO's media API to transcode and save the output file to cloud. I have two URLs {URL1:input to dolby; URL2: to store output from dolby}. And both the URLs are signed URLs from the same cloud.
I tried using some java code to accomplish this but in the end I still can't get the result.
Here is the code:
#PostMapping("/transcode")
public String Video_Transcode1() throws IOException, JSONException {
OkHttpClient client = new OkHttpClient();
String data=generate_Access_token( );
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"inputs\":[{\"source\":\"https://vb-object-storage.ap-south-1.linodeobjects.com/The%20Hindu%20Daily%20News%20Analysis%20__%203rd%20July%202022%20__%20UPSC%20Current%20Affairs%20__%20Prelims%20%2722%20%26%20Mains%20%2722%28360%29.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220707T073322Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Credential=ZVADROBVHWLK1FOYT225%2F20220707%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Signature=0aa4b388ea3197dd8a03253f5f7313b4209b8acf5e0a4308dc5e543801d22c73\"}],\"outputs\":[{\"kind\":\"mp4\",\"destination\":\"https://vb-object-storage.ap-south-1.linodeobjects.com/The%20Hindu%20Daily%20News%20Analysis%20__%203rd%20July%202022%20__%20UPSC%20Current%20Affairs%20__%20Prelims%20%2722%20%26%20Mains%20%2722%28360%29.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220707T073322Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Credential=ZVADROBVHWLK1FOYT225%2F20220707%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Signature=0aa4b388ea3197dd8a03253f5f7313b4209b8acf5e0a4308dc5e543801d22c73\"}]}");
Request request = new Request.Builder()
.url("https://api.dolby.com/media/transcode")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization","Bearer "+data)
.build();
Response response = client.newCall(request).execute();
return response.toString();
}
Here the data is generated from another function (ie:Access token)
I have encoded two URLs as json here:
RequestBody body = RequestBody.create(mediaType, "{\"inputs\":[{\"source\":\"https://vb-object-storage.ap-south-1.linodeobjects.com/The%20Hindu%20Daily%20News%20Analysis%20__%203rd%20July%202022%20__%20UPSC%20Current%20Affairs%20__%20Prelims%20%2722%20%26%20Mains%20%2722%28360%29.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220707T073322Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Credential=ZVADROBVHWLK1FOYT225%2F20220707%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Signature=0aa4b388ea3197dd8a03253f5f7313b4209b8acf5e0a4308dc5e543801d22c73\"}],\"outputs\":[{\"kind\":\"mp4\",\"destination\":\"https://vb-object-storage.ap-south-1.linodeobjects.com/The%20Hindu%20Daily%20News%20Analysis%20__%203rd%20July%202022%20__%20UPSC%20Current%20Affairs%20__%20Prelims%20%2722%20%26%20Mains%20%2722%28360%29.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220707T073322Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Credential=ZVADROBVHWLK1FOYT225%2F20220707%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Signature=0aa4b388ea3197dd8a03253f5f7313b4209b8acf5e0a4308dc5e543801d22c73\"}]}");
Is there any look around to bring the solution.
it looks like both signed URLs might be GET urls?
For the Dolby.io Transcode API, your inputs should be GET signed urls and your outputs should be PUT signed URLs.
Additionally, it also looks like you are using the same input path/filename and output path/filename:
https://vb-object-storage.ap-south-1.linodeobjects.com/The%20Hindu%20Daily%20News%20Analysis%20__%203rd%20July%202022%20__%20UPSC%20Current%20Affairs%20__%20Prelims%20%2722%20%26%20Mains%20%2722%28360%29.mp4
You will want to use different paths for input and output, something like:
https://vb-object-storage.ap-south-1.linodeobjects.com/output/outputfile.mp4
(note the "output" added to the path and the change of the output filename)
Using intellij, Java and restassured:
I am sending the request as needed and I added this to my code:
public static Response PostInstinctQuery() throws IOException, ParseException {
PrintStream fileOutPutStream = new PrintStream(new File("request_log.txt"));
config = config().logConfig(new LogConfig().defaultStream(fileOutPutStream));
RestAssured.baseURI = BASEURI;
RequestSpecification request = RestAssured.given();
//Headers
request.header("Key",key);
request.body(getJson());
request.log().all();
Response response = request.post(PATH);
return response;
}
That is save the request in "request_log.txt" file.
so what is the problem ? the file shows the same request over and over.
If I use TestNG diff data then I would expect that the file will contain all the diff request.
And I really want the request to be able to save in a String variable for assert / report purposes also.
Thanks!
I think you can use RequestLoggingFilter to config one time, no need log().all().
OutputStream file = new FileOutputStream("request_log.txt");
PrintStream stream = new PrintStream(file, true);
RestAssured.filters(RequestLoggingFilter.logRequestTo(stream));
I am trying to download a file from rest API, I am writing code in Java and react. but when i call that rest api it is not downloading that file instead gives me some garbage
#POST
#Path("/{loginId}")
#Produces(MULTIPART_FORM_DATA)
#Consumes(APPLICATION_JSON)
public Response downloadExportedFile(#PathParam("loginId") String loginId, ExportFileDTO fileDetails) {
File exportFolder = new File("C://directory");
File[] listOfFiles = exportFolder.listFiles();
for (File listOfFile : listOfFiles) {
if (listOfFile.getName().equals(fileDetails.getFileName())) {
InputStream is = new FileInputStream(listOfFile.getAbsolutePath());
byte[] buffer = IOUtils.toByteArray(is);
return Response.ok(listOfFile)
.header("content-disposition", "attachment; filename=" + new File(listOfFile.getName()).getName())
.type(MediaType.APPLICATION_OCTET_STREAM_TYPE).build();
}
}
It should download the file instead it is giving me output as
PK!b�h^�[Content_Types].xml �(����N�0E�H�C�-Jܲ#5��Q>�ēƪc[�ii����B�j7���{2��h�nm���ƻR����U^7/���%��rZY�#1__�f��q��R4D�AJ�h>����V�ƹ�Z�9����NV�8ʩ����ji){^��-I�"{�v^�P!XS)bR�r��K�s(�3�`c�0��������7M4�����ZƐk+�|\|z�(���P��6h_-[�#�!���Pk���2n�}�?�L��� ��%���d����dN"m,�ǞDO97�~��ɸ8�O�c|n���E������B��!$}�����;{���[����2���PK!�U0#�L_rels/.rels �(���MO�0��H�����ݐBKwAH�!T~�I����$ݿ'T�G�~����<���!��4��;#�w����qu*&r�Fq���v�����GJy(v��*����K��#F��D��.W ��=��Z�MY�b���BS�����7��ϛז��
?�9L�ҙ�sbgٮ|�l!��USh9i�b�r:"y_dl��D���|-N��R"4�2�G�%��Z�4�˝y�7 ë��ɂ�����PK!
You have to change the associated mimetype by changing the the parameter of the #Produces annotation which basically describes what type of data you transmit in your response.
It should become:
#Produces("application/vnd.ms-excel")
According to this other stackoverflow question you should change the #Produces annotation to #Produces(MediaType.APPLICATION_OCTET_STREAM).
According to this second stackoverflow question you are asking an impossible question.
Out of curiosity I reproduced your problem here : see the full gist
If you change #POST to #GET it starts working
If you keep #POST, it has to be posted from a real form and can't post application/json
Finally, posting application/json means React is doing a programmatic XmlHTTPRequest. The above gist shall convince you there is no user prompt in that case
When you say it 'is giving me output', you're not telling where and how the post was requested . You will have to adapt that part.
actually It is APPLICATION_OCTET_STREAM response for a file. we have to handle download functionality at client side AS per Nate's answer here, the response of Ajax request is not recognized by a browser as a file. It will behave in the same way for all Ajax responses. You need to trigger the download popup manually.
downloadFile(fileDetails) {
let username = getUserName();
return fetch(`/files/${username}`, {
method: 'POST',
body: JSON.stringify(fileDetails)
}).then(response => {
return response.blob();
}).then(response => {
let blob = new Blob([response], {type: 'application/octet-stream'});
let fileUrl = window.URL.createObjectURL(blob);
Files.triggerDownload(fileUrl, fileDetails.fileName);
}).catch((error) => {
//myerror
});
}
static triggerDownload(url, fileName) {
let a = document.createElement('a');
a.setAttribute('href', url);
a.setAttribute('download', fileName);
a.click();
}
This will download the file at client machine
I cannot find out why the mp3 file is different after download from my server than original one saved previously there.
This is my controller method. The content of file (byte[] content) is identical with original file on this stage - the original file is the same as file retrieved from database (checked in debugger).
#ResponseBody
#RequestMapping(method = RequestMethod.GET, value = "/{sampleId}/file")
public HttpEntity<byte[]> getFile(#PathVariable Long sampleId) {
ResourceEntity resourceEntity = testSampleRepository.getFile(sampleId);
byte[] content = resourceEntity.getContent();
String fileName = resourceEntity.getFileName();
HttpHeaders header = new HttpHeaders();
header.setContentType(new MediaType("audio", "mpeg"));
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(content.length);
return new HttpEntity<byte[]>(content, header);
}
This is how files differ (the left is original one):
Why passing using HTTP distors my file? Should mediaTypes enforce certain encoding? (there was no difference with "audio/mpeg" mediaType and without it).
It should work, if you set the produces = "application/octet-stream" attribute (MediaType.APPLICATION_OCTET_STREAM). Otherwise, you are trapped by Spring's converter framework.
You may want to have a look here, seems your problem is very similar: Spring MVC: How to return image in #ResponseBody? .
My application using Oauth for basecam api. I am trying to get Httpresponse into json format but it revert into plain html (text/html) content-type. so there is no method to parse HTML content and get the token from basecamp. This is not homework but a small R&D to quick start Oauth protocol. as am new to oauth.
//HERE -> final String JSON_CONTENT = "application/json"
String contentType = OAuthConstants.JSON_CONTENT;
if (response.getEntity().getContentType() != null) {
contentType = response.getEntity().getContentType().getValue();
//BELOW -> getting contentType is in "text/html; utf-8
System.out.println(response.getEntity().getContentType().getValue()); //text/html; charset=utf-8
}
if (contentType.contains(OAuthConstants.JSON_CONTENT)) {
return handleJsonResponse(response);
} else
if (contentType.contains(OAuthConstants.URL_ENCODED_CONTENT)) {
return handleURLEncodedResponse(response);
} else
if (contentType.contains(OAuthConstants.XML_CONTENT)) {
return handleXMLResponse(response);
}
else {
// Unsupported Content type
throw new RuntimeException(
"Cannot handle "
+ contentType
+ " content type. Supported content types include JSON, XML and URLEncoded");
}
So above lines explain very well that control won't come is json, xml or url_encoded if-else. Si either i need to parse text/html into json or xml response or i have to create another method name handleHtmlResponse(). what way it would be continent to get contentType.
After the response is set with all the data(header, body ...), commit it by calling ServletResponse#flushBuffer.