Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include underlying request in errors #830

Merged
merged 3 commits into from Apr 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -469,11 +469,16 @@ instance.interceptors.request.use(function () {/*...*/});
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made, but the server responded with a status code
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
Expand Down
9 changes: 5 additions & 4 deletions lib/adapters/http.js
Expand Up @@ -172,13 +172,14 @@ module.exports = function httpAdapter(config) {

// make sure the content length is not over the maxContentLength if specified
if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', config));
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
config, null, lastRequest));
}
});

stream.on('error', function handleStreamError(err) {
if (aborted) return;
reject(enhanceError(err, config));
reject(enhanceError(err, config, null, lastRequest));
});

stream.on('end', function handleStreamEnd() {
Expand All @@ -196,14 +197,14 @@ module.exports = function httpAdapter(config) {
// Handle errors
req.on('error', function handleRequestError(err) {
if (aborted) return;
reject(enhanceError(err, config));
reject(enhanceError(err, config, null, req));
});

// Handle request timeout
if (config.timeout && !timer) {
timer = setTimeout(function handleRequestTimeout() {
req.abort();
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED'));
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
aborted = true;
}, config.timeout);
}
Expand Down
5 changes: 3 additions & 2 deletions lib/adapters/xhr.js
Expand Up @@ -84,15 +84,16 @@ module.exports = function xhrAdapter(config) {
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config));
reject(createError('Network Error', config, null, request));

// Clean up request
request = null;
};

// Handle timeout
request.ontimeout = function handleTimeout() {
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED'));
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
request));

// Clean up request
request = null;
Expand Down
7 changes: 4 additions & 3 deletions lib/core/createError.js
Expand Up @@ -3,15 +3,16 @@
var enhanceError = require('./enhanceError');

/**
* Create an Error with the specified message, config, error code, and response.
* Create an Error with the specified message, config, error code, request and response.
*
* @param {string} message The error message.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
@ @param {Object} [request] The request.
@ @param {Object} [response] The response.
* @returns {Error} The created error.
*/
module.exports = function createError(message, config, code, response) {
module.exports = function createError(message, config, code, request, response) {
var error = new Error(message);
return enhanceError(error, config, code, response);
return enhanceError(error, config, code, request, response);
};
4 changes: 3 additions & 1 deletion lib/core/enhanceError.js
Expand Up @@ -6,14 +6,16 @@
* @param {Error} error The error to update.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
@ @param {Object} [request] The request.
@ @param {Object} [response] The response.
* @returns {Error} The error.
*/
module.exports = function enhanceError(error, config, code, response) {
module.exports = function enhanceError(error, config, code, request, response) {
error.config = config;
if (code) {
error.code = code;
}
error.request = request;
error.response = response;
return error;
};
1 change: 1 addition & 0 deletions lib/core/settle.js
Expand Up @@ -19,6 +19,7 @@ module.exports = function settle(resolve, reject, response) {
'Request failed with status code ' + response.status,
response.config,
null,
response.request,
response
));
}
Expand Down
8 changes: 6 additions & 2 deletions test/specs/core/createError.spec.js
@@ -1,11 +1,15 @@
var createError = require('../../../lib/core/createError');

describe('core::createError', function() {
it('should create an Error with message, config, and code', function() {
var error = createError('Boom!', { foo: 'bar' }, 'ESOMETHING');
it('should create an Error with message, config, code, request and response', function() {
var request = { path: '/foo' };
var response = { status: 200, data: { foo: 'bar' } };
var error = createError('Boom!', { foo: 'bar' }, 'ESOMETHING', request, response);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe('Boom!');
expect(error.config).toEqual({ foo: 'bar' });
expect(error.code).toBe('ESOMETHING');
expect(error.request).toBe(request);
expect(error.response).toBe(response);
});
});
9 changes: 7 additions & 2 deletions test/specs/core/enhanceError.spec.js
@@ -1,11 +1,16 @@
var enhanceError = require('../../../lib/core/enhanceError');

describe('core::enhanceError', function() {
it('should add config and code to error', function() {
it('should add config, config, request and response to error', function() {
var error = new Error('Boom!');
enhanceError(error, { foo: 'bar' }, 'ESOMETHING');
var request = { path: '/foo' };
var response = { status: 200, data: { foo: 'bar' } };

enhanceError(error, { foo: 'bar' }, 'ESOMETHING', request, response);
expect(error.config).toEqual({ foo: 'bar' });
expect(error.code).toBe('ESOMETHING');
expect(error.request).toBe(request);
expect(error.response).toBe(response);
});

it('should return error', function() {
Expand Down
7 changes: 6 additions & 1 deletion test/specs/core/settle.spec.js
Expand Up @@ -48,13 +48,17 @@ describe('core::settle', function() {
});

it('should reject promise if validateStatus returns false', function() {
var req = {
path: '/foo'
};
var response = {
status: 500,
config: {
validateStatus: function() {
return false;
}
}
},
request: req
};
settle(resolve, reject, response);
expect(resolve).not.toHaveBeenCalled();
Expand All @@ -63,6 +67,7 @@ describe('core::settle', function() {
expect(reason instanceof Error).toBe(true);
expect(reason.message).toBe('Request failed with status code 500');
expect(reason.config).toBe(response.config);
expect(reason.request).toBe(req);
expect(reason.response).toBe(response);
});

Expand Down
7 changes: 4 additions & 3 deletions test/specs/requests.spec.js
Expand Up @@ -49,15 +49,16 @@ describe('requests', function () {
var reason = rejectSpy.calls.first().args[0];
expect(reason instanceof Error).toBe(true);
expect(reason.config.method).toBe('get');
expect(reason.config.url).toBe('http://thisisnotaserver');
expect(reason.config.url).toBe('http://thisisnotaserver/foo');
expect(reason.request).toEqual(jasmine.any(XMLHttpRequest));

// re-enable jasmine.Ajax
jasmine.Ajax.install();

done();
};

axios('http://thisisnotaserver')
axios('http://thisisnotaserver/foo')
.then(resolveSpy, rejectSpy)
.then(finish, finish);
});
Expand Down