diff --git a/spec/observables/dom/ajax-spec.ts b/spec/observables/dom/ajax-spec.ts index 8ef53dc638d..143e90227e7 100644 --- a/spec/observables/dom/ajax-spec.ts +++ b/spec/observables/dom/ajax-spec.ts @@ -229,6 +229,34 @@ describe('Observable.ajax', () => { expect(complete).to.be.true; }); + it('should fail if fails to parse response', () => { + let error; + const obj = { + url: '/flibbertyJibbet', + responseType: 'json', + method: '' + }; + + Rx.Observable.ajax(obj) + .subscribe((x: any) => { + throw 'should not next'; + }, (err: any) => { + error = err; + }, () => { + throw 'should not complete'; + }); + + MockXMLHttpRequest.mostRecent.respondWith({ + 'status': 207, + 'contentType': '', + 'responseType': '', + 'responseText': 'Wee! I am text, but should be valid JSON!' + }); + + expect(error instanceof SyntaxError).to.be.true; + expect(error.message).to.equal('Unexpected token W in JSON at position 0'); + }); + it('should fail on 404', () => { let error; const obj = { @@ -294,6 +322,36 @@ describe('Observable.ajax', () => { expect(error.status).to.equal(300); }); + it('should fail if fails to parse error response', () => { + let error; + const obj = { + url: '/flibbertyJibbet', + normalizeError: (e: any, xhr: any, type: any) => { + return xhr.response || xhr.responseText; + }, + responseType: 'json', + method: '' + }; + + Rx.Observable.ajax(obj).subscribe(x => { + throw 'should not next'; + }, (err: any) => { + error = err; + }, () => { + throw 'should not complete'; + }); + + MockXMLHttpRequest.mostRecent.respondWith({ + 'status': 404, + 'contentType': '', + 'responseType': '', + 'responseText': 'Wee! I am text, but should be valid JSON!' + }); + + expect(error instanceof SyntaxError).to.be.true; + expect(error.message).to.equal('Unexpected token W in JSON at position 0'); + }); + it('should succeed no settings', () => { const expected = JSON.stringify({ foo: 'bar' }); @@ -1037,8 +1095,6 @@ class MockXMLHttpRequest { protected defaultResponseValue() { if (this.async === false) { this.response = this.responseText; - } else { - throw new Error('unhandled type "' + this.responseType + '"'); } } @@ -1054,8 +1110,9 @@ class MockXMLHttpRequest { }; this.status = response.status || 200; this.responseText = response.responseText; + const responseType = response.responseType !== undefined ? response.responseType : this.responseType; if (!('response' in response)) { - switch (this.responseType) { + switch (responseType) { case 'json': this.jsonResponseValue(response); break; diff --git a/src/observable/dom/AjaxObservable.ts b/src/observable/dom/AjaxObservable.ts index 427d1f1eee0..8f6b1d9e3d5 100644 --- a/src/observable/dom/AjaxObservable.ts +++ b/src/observable/dom/AjaxObservable.ts @@ -217,8 +217,11 @@ export class AjaxSubscriber extends Subscriber { this.done = true; const { xhr, request, destination } = this; const response = new AjaxResponse(e, xhr, request); - - destination.next(response); + if (response.response === errorObject) { + destination.error(errorObject.e); + } else { + destination.next(response); + } } private send(): XMLHttpRequest { @@ -316,7 +319,12 @@ export class AjaxSubscriber extends Subscriber { if (progressSubscriber) { progressSubscriber.error(e); } - subscriber.error(new AjaxTimeoutError(this, request)); //TODO: Make betterer. + const ajaxTimeoutError = new AjaxTimeoutError(this, request); //TODO: Make betterer. + if (ajaxTimeoutError.response === errorObject) { + subscriber.error(errorObject.e); + } else { + subscriber.error(ajaxTimeoutError); + } }; xhr.ontimeout = xhrTimeout; (xhrTimeout).request = request; @@ -342,7 +350,12 @@ export class AjaxSubscriber extends Subscriber { if (progressSubscriber) { progressSubscriber.error(e); } - subscriber.error(new AjaxError('ajax error', this, request)); + const ajaxError = new AjaxError('ajax error', this, request); + if (ajaxError.response === errorObject) { + subscriber.error(errorObject.e); + } else { + subscriber.error(ajaxError); + } }; xhr.onerror = xhrError; (xhrError).request = request; @@ -375,7 +388,12 @@ export class AjaxSubscriber extends Subscriber { if (progressSubscriber) { progressSubscriber.error(e); } - subscriber.error(new AjaxError('ajax error ' + status, this, request)); + const ajaxError = new AjaxError('ajax error ' + status, this, request); + if (ajaxError.response === errorObject) { + subscriber.error(errorObject.e); + } else { + subscriber.error(ajaxError); + } } } }; @@ -458,15 +476,19 @@ export class AjaxError extends Error { } } +function parseJson(xhr: XMLHttpRequest) { + if ('response' in xhr) { + //IE does not support json as responseType, parse it internally + return xhr.responseType ? xhr.response : JSON.parse(xhr.response || xhr.responseText || 'null'); + } else { + return JSON.parse(xhr.responseText || 'null'); + } +} + function parseXhrResponse(responseType: string, xhr: XMLHttpRequest) { switch (responseType) { case 'json': - if ('response' in xhr) { - //IE does not support json as responseType, parse it internally - return xhr.responseType ? xhr.response : JSON.parse(xhr.response || xhr.responseText || 'null'); - } else { - return JSON.parse(xhr.responseText || 'null'); - } + return tryCatch(parseJson)(xhr); case 'xml': return xhr.responseXML; case 'text':