Reputation: 4472
Here is my scenario: I want to get some external resource (binary file) using request library and pipe it to the client of my own application. If response code is != 200 or there are problems reaching remote server, I want to intercept and provide custom error message instead. Ideally, if response is fine, I want original headers to be preserved.
I was able to achieve that with the first piece of code I've pasted below. However, my whole application is based on Promise API so I wanted to make it consistent and wrap it in promise too. And when I do that, it no longer works. Firstly, I tried to achieve that with request-promise, without success. Then I tried to prepare very simple example on my own, still no luck.
Working example
var r = request.get('http://foo.bar');
r.on('response', result => {
if (result.statusCode === 200) {
r.pipe(res);
} else {
res.status(500).send('custom error message')
}
});
r.on('error', error => res.status(500).send('custom error message');
Not working example
var r;
return new Promise((resolve, reject) => {
r = request.get('http://foo.bar');
r.on('response', result => {
if (result.statusCode === 200) {
resolve();
} else {
reject()
}
});
r.on('error', reject);
}).then(() => {
r.pipe(res);
}).catch(() => {
res.status(500).json('custom error message');
});
By 'not working' I mean - no response is delivered, request is pending until timeout.
I've changed the code to call .pipe()
on result passed to resolve
instead of r
. It responds to client, but response is empty then.
At the end, I've tried replacing request lib with simply http.get()
. And with that, server returns file to the client, but headers (like Content-Type
or Content-Length
) are missing.
I've googled a lot, tried several request versions... and nothing is working.
Upvotes: 0
Views: 2885
Reputation: 138457
The problem is that when "response"
is triggered, you create a new promise that resolves immeadiately, but the then
callback is always executed asynchronously, and when it gets called the file has arrived at the server, and there is no data flowing through the stream anymore. Instead you could just use the body
parameter of the callback:
request.get('http://foo.bar', function(request, response, body) {
if(response.statusCode === 200) {
res.end(body);
} else {
res.status(500).end();
}
});
For working with streams request
seems a bit buggy, axios
seems to do it better:
axios.get("http://foo.bar"', {
validateStatus: status => status === 200,
responseType: "stream"
}).then(({data: stream}) => {
stream.pipe(res);
}).catch(error => {
res.status(500).json(error);
});
Upvotes: 3