I currently have the following working piece of code (angular but applies to any JS framework):
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
The above is called after a form was pleted and the user has clicked on "submit" (the real situation is a bit more plex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.
If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.
How can I refrain from making two requests if the requests is successful?
- Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
- Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have
"CONTENT-DISPOSITION" -> "attachment"
in the HTTP header of the successful response
Update: additional information about the architecture as requested by Emile: In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary plexity.
While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF. So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...
Here's the adapted code:
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url+'?check'
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
On the backend side (I use Play framework/Scala)
def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
myForm.bindFromRequest.fold(
e => BadRequest(myErrors),
v => if(onlyDoCheck) Ok(simpleOkResponse) else Ok(doComputationgeneratefileandoutputfile)
)
}
I currently have the following working piece of code (angular but applies to any JS framework):
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
The above is called after a form was pleted and the user has clicked on "submit" (the real situation is a bit more plex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.
If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.
How can I refrain from making two requests if the requests is successful?
- Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
- Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have
"CONTENT-DISPOSITION" -> "attachment"
in the HTTP header of the successful response
Update: additional information about the architecture as requested by Emile: In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary plexity.
While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF. So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...
Here's the adapted code:
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url+'?check'
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
On the backend side (I use Play framework/Scala)
def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
myForm.bindFromRequest.fold(
e => BadRequest(myErrors),
v => if(onlyDoCheck) Ok(simpleOkResponse) else Ok(doComputationgeneratefileandoutputfile)
)
}
Share
Improve this question
edited Jul 29, 2016 at 20:27
John
asked Jul 29, 2016 at 19:08
JohnJohn
5,36410 gold badges39 silver badges47 bronze badges
10
- 1 have you seen: stackoverflow./questions/20830309/… – Iceman Commented Jul 29, 2016 at 19:14
- url is in an object closure and methods are chain. I haven't used angular, but could you jump to that location url object with: window.location = this.url; – Leroy Thompson Commented Jul 29, 2016 at 19:16
- 1 @iceman If I understood you right, yes. I edited the question for more clarity – John Commented Jul 29, 2016 at 19:24
- 1 See my edited answer to understand how to handle the response – Emile Bergeron Commented Jul 29, 2016 at 19:35
-
1
@John 's answer using HTML5
download
is probably your only soln. Note that Safari, IE, Safari-iOS, Opera-mini and some such browsers dont support this particular spec. – Iceman Commented Jul 29, 2016 at 19:48
1 Answer
Reset to default 9The real deal
The best you could do is split your endpoint.
- One for the form and the convenience of having errors without refresh.
- Then, on success, redirect to your other endpoint which only downloads the file.
If the file was on the disk and wasn't auto-generated and required to be authenticated to be downloaded, you could hide the file behind a normal endpoint, do the checks, and return the file using X-Accel-Redirect
header with nginx or X-Sendfile
using apache.
The hack
Disclaimer: This is more of a hack than the best solution. As mention by @Iceman, Safari, IE, Safari-iOS, Opera-mini and some such browsers don't support this particular spec.
In your server-side endpoint, if the file is available without errors, you can set the header to the content-type of the file (like 'application/pdf'
) so the download will starts automatically.
If there are errors, don't set the header and return a json of the errors to inform the user through javascript.
Since we don't know what's behind, here's a python (Django) example:
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=your_filename.pdf'
response.write(repport.printReport(participantId))
return response
You can handle the response in the ajax success callback:
$.ajax({
url: 'endpoint.php',
success: function(data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "filename.pdf";
link.click();
}
});
You could also try the jQuery fileDownload plugin mentioned in this answer.