In my MEAN application I need to provide a link to download a file, the link must be hidden and not accessible by unauthorized users.
So I came up with this idea of keeping the files inside the server directory and let Angular.js send with ng-click="download()" an $HTTP request to express.js with the file ID to download, and (possibly) the user id/pwd.
First of all is this a safe solution?
Second, here is my code that doesn't work, there are no errors whatsoever, but I can't even get the download dialog box to open:
Client Side
$scope.download=function(){
$http({method:'GET', url:'/download/'+image[0]['_id']}).
success(function(data, status, headers, config) {
var element = angular.element('<a/>');
element.attr({
href: 'data:attachment/csv;charset=utf-8,' + encodeURI(data),
target: '_blank',
download:'test.csv'
})[0].click();
}).
error(function(data, status, headers, config) {
});
}
Server Side
app.namespace('/download/:documentID*', function() {
app.all('/', function(req, res, next){
res.download('images/download/test.tif', 'test.tif', function(err){
if (err) {
} else {
next();
}
});
});
})
Last time I checked you couldn't trigger a download via ajax. ;-)
You'll need to create a <a> with e.g. target="_blank" so it opens up in a new tab/window. Don't know about your authentication though, I wouldn't send them in cleartext. You could work around that by checking the credentials in your ajax request and then open a new tab/window so that the file can be downloaded with some kind of one-time token. You'll need some server-side changes ofc.
Related
So my scenario is a user clicks a button on a web app, this triggers a server side POST request to an internal (i.e non public) API sitting on another server in the same network, this should return a PDF to my server which will proxy (pipe) it back to the user.
I want to just proxy the PDF body content directly to the client without creating a tmp file.
I have this code which works using the npm request module but it does not feel right:
var pdfRequest = request(requestOptions);
pdfRequest.on('error', function (err) {
utils.sendErrorResponse(500, 'PROBLEM PIPING PDF DOWNLOAD: ' + err, res);
});
pdfRequest.on('response', function (resp) {
if (resp.statusCode === 200) {
pdfRequest.pipe(res);
} else {
utils.sendErrorResponse(500, 'PROBLEM PIPING PDF DOWNLOAD: RAW RESP: ' + JSON.stringify(resp), res);
}
});
Is the the correct way to pipe the PDF response?
Notes:
I need to check the status code to conditionally handle errors, the payload for the POST is contained in the requestOptions (I know this part is all correct).
I would like to keep using the request module
I defiantly do not want to be creating any temp files
If possible I would also like to modify the content disposition header to set a custom filename, i know how to do this without using pipes
I create an app with requirement of template files being in two folders - first is available without restrictions and second is available only for logged in users.
Things are complicating when somebody refresh the page and session expire before. Angular throws error (e.g. 401) in console while loading template file which I want to avoid. Is there a way to catch that event and access response status to for e.g. redirect the page?
For your case, we can catch the error and redirect to a page with the help of $location.url(). Here is the concept. Now i have a controller and in that, i have success function and error function. Whenever we get an error, the error function will run and we can pass the link of the page you want to redirect.
$http.get(url,[params])
.success(function(data, status, headers, config){
// bind your data to scope
})
.error(function(data, status, headers, config) {
$location.url('/404');
});
By the use of $routeProvider, you can configure the function something like
this :
$routeProvider
.when('/404', {
templateUrl: '404.html',
controller: 'yourcontroller'
});
And you can see the description for $location.url() here
Hope it works
I need to embed a PDF document into html but the document needs a token authentication that is passed in as a header.
headers: {'X-Authentication': t}
Where t is the token that I retrieved after Server authentication.
How can I get the document on the client side and display it in an iframe?
I am using angular to make the REST calls to the server.
I'm not entirely sure this will work but here's how I'd approach it...
Assuming you can get the PDF binary in your app, do so like this
$http.get('/path/to/pdf', {
responseType: 'blob',
headers: {
'X-Authentication': t
},
transformResponse: function(data) {
// don't forget to inject $window
return ($window.URL || $window.webkitURL).createObjectURL(data);
}
})
This will return an $http promise (like a normal promise but with success and error methods) where the data is resolved as a URI you can use in your iframe's src attribute. For example...
.success(function(uri) {
$scope.iframeUri = uri;
});
See https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
I have a Backbone.js application running with this generator https://github.com/yeoman/generator-backbone. I need to make a POST request to a service running on a different server and domain. On my model I have the following:
urlRoot: 'https://custom.com/',
saveData: function(params) {
this.set(params);
if(this.isValid()) {
this.save(null, {
url: 'api/v1/data',
success: function(model, response, options) {
// success
},
error: function(model, response, options) {
// error
}
});
}
}
When I try posting the data, it posts the data to http://localhost:9000/api/v1/data. Any idea why Backbone chooses to POST to the server it is runnning on, instead of the settings I supplied (https://custom.com/api/v1/data)?
On my server, I'm running Restify on top of NodeJS (I'm allowing origin from any client for testing purposes). Here's how I setup CORS:
// Setup CORS
api.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
return next();
});
I doubt there is a problem with the server, since it isn't even being reached by the client because somehow the specified URL automatically maps to http://localhost:9000/api/v1/data instead of https://custom.com/api/v1/data.
UPDATE: If i do it "manually" with jQuery (without using Backbone's API), it works:
saveData: function() {
$.ajax({
type: 'POST',
data: this.toJSON(),
url: 'https://custom.com/api/v1/data'
})
.success(function() {
// success
})
.fail(function() {
// fail
});
}
Is there anyway to do it using Backbone's API?
Passing a url as an option to Backbone.Model.prototype.save will circumvent the url generation logic (i.e. urlRoot) and assume the url option is the full path (including the domain if not POSTing to the current domain).
You should instead set the urlRoot to the path of the entity (or whatever your backend platform may call it), so in your case it should be:
var MyModel = Backbone.Model.extend({
urlRoot: 'https://custom.com/api/v1/data'
});
var myModel = new MyModel();
myModel.save(); // POST https://custom.com/api/v1/data
// Assuming that returns an id of '123';
myModel.save(); // PUT https://custom.com/api/v1/data/123
myModel.destroy(); // DELETE https://custom.com/api/v1/data/123
If your server isn't set up to handle Backbone style RESTful endpoints then you probably just want to override Backbone.Model.prototype.url instead of urlRoot.
If you only wanna test calls to the server, you can download a chrome extension. It will allow you to make calls to a different domain. Here : https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi
Post request to a different domain will not work from browser. Better way to handle this is you can set some redirect rules in server.
When I use GET, everything works fine. However, I struggle to use POST to achieve the same effect. Here are the code I have tried:
1.
app.post("/download", function (req, res) {
res.download("./path");
});
2.
app.post("/download", function (req, res) {
res.attachment("./path");
res.send("ok");
});
3.
app.post("/download", function (req, res) {
res.sendFile("./path");
});
None of them work. What is the correct way to do this?
EDIT:
I submit a POST request through a HTML form to /download. ./path is a static file. When I use code in method 1, I can see the correct response header and response body in the developer tool. But the browser does not prompt a download.
This might not be exactly what you want, but I have been having the same trouble.
This is what I did in the end:
Client - See EDIT below for updated client code
$http.post('/download', /**your data**/ ).
success(function(data, status, headers, config) {
$window.open('/download'); //does the download
}).
error(function(data, status, headers, config) {
console.log('ERROR: could not download file');
});
Server
// Receive data from the client to write to a file
app.post("/download", function (req, res) {
// Do whatever with the data
// Write it to a file etc...
});
// Return the generated file for download
app.get("/download", function (req, res) {
// Resolve the file path etc...
res.download("./path");
});
Alternatively, have you just tried calling $window.open(/download); from the HTML? This was the main reason why my download did not start. It returned in the XHR and I could see the data, but also did not prompt a download.
*EDIT:
The client code was not accurate, after some more testing it turned out that I only needed to do the following on the client:
// NOTE: Ensure that the data to be downloaded has
// already been packaged/created and is available
$window.open('/download'); //does the download