Skip to content

Commit d8231e2

Browse files
mykltbenlesh
authored andcommitted
fix(AjaxObservable): notify with error if fails to parse json response (#3139)
* fix(AjaxObservable): notify with error if fails to parse json response Catch error when parsing json response and notify observer with thrown error. JSON could be invalid if proxy overtakes the response and in IE responseType is empty. closes#3138 * chore(lint): remove semicolon
1 parent e73881f commit d8231e2

2 files changed

Lines changed: 95 additions & 16 deletions

File tree

‎spec/observables/dom/ajax-spec.ts‎

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,34 @@ describe('ajax', () => {
246246
expect(complete).to.be.true;
247247
});
248248

249+
it('should fail if fails to parse response',()=>{
250+
leterror: any;
251+
constobj={
252+
url: '/flibbertyJibbet',
253+
responseType: 'json',
254+
method: ''
255+
};
256+
257+
ajax(obj)
258+
.subscribe((x: any)=>{
259+
throw'should not next';
260+
},(err: any)=>{
261+
error=err;
262+
},()=>{
263+
throw'should not complete';
264+
});
265+
266+
MockXMLHttpRequest.mostRecent.respondWith({
267+
'status': 207,
268+
'contentType': '',
269+
'responseType': '',
270+
'responseText': 'Wee! I am text, but should be valid JSON!'
271+
});
272+
273+
expect(errorinstanceofSyntaxError).to.be.true;
274+
expect(error.message).to.equal('Unexpected token W in JSON at position 0');
275+
});
276+
249277
it('should fail on 404',()=>{
250278
leterror: any;
251279
constobj={
@@ -312,6 +340,36 @@ describe('ajax', () => {
312340
expect(complete).to.be.true;
313341
});
314342

343+
it('should fail if fails to parse error response',()=>{
344+
leterror: any;
345+
constobj={
346+
url: '/flibbertyJibbet',
347+
normalizeError: (e: any,xhr: any,type: any)=>{
348+
returnxhr.response||xhr.responseText;
349+
},
350+
responseType: 'json',
351+
method: ''
352+
};
353+
354+
ajax(obj).subscribe(x=>{
355+
throw'should not next';
356+
},(err: any)=>{
357+
error=err;
358+
},()=>{
359+
throw'should not complete';
360+
});
361+
362+
MockXMLHttpRequest.mostRecent.respondWith({
363+
'status': 404,
364+
'contentType': '',
365+
'responseType': '',
366+
'responseText': 'Wee! I am text, but should be valid JSON!'
367+
});
368+
369+
expect(errorinstanceofSyntaxError).to.be.true;
370+
expect(error.message).to.equal('Unexpected token W in JSON at position 0');
371+
});
372+
315373
it('should succeed no settings',()=>{
316374
constexpected=JSON.stringify({foo: 'bar'});
317375

@@ -1104,8 +1162,6 @@ class MockXMLHttpRequest {
11041162
protecteddefaultResponseValue(){
11051163
if(this.async===false){
11061164
this.response=this.responseText;
1107-
}else{
1108-
thrownewError('unhandled type "'+this.responseType+'"');
11091165
}
11101166
}
11111167

@@ -1121,8 +1177,9 @@ class MockXMLHttpRequest {
11211177
};
11221178
this.status=response.status||200;
11231179
this.responseText=response.responseText;
1180+
constresponseType=response.responseType!==undefined ? response.responseType : this.responseType;
11241181
if(!('response'inresponse)){
1125-
switch(this.responseType){
1182+
switch(responseType){
11261183
case'json':
11271184
this.jsonResponseValue(response);
11281185
break;

‎src/internal/observable/dom/AjaxObservable.ts‎

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,11 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
221221
this.done=true;
222222
const{ xhr, request, destination }=this;
223223
constresponse=newAjaxResponse(e,xhr,request);
224-
225-
destination.next(response);
224+
if(response.response===errorObject){
225+
destination.error(errorObject.e);
226+
}else{
227+
destination.next(response);
228+
}
226229
}
227230

228231
privatesend(): XMLHttpRequest{
@@ -320,7 +323,12 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
320323
if(progressSubscriber){
321324
progressSubscriber.error(e);
322325
}
323-
subscriber.error(newAjaxTimeoutError(this,request));//TODO: Make betterer.
326+
constajaxTimeoutError=newAjaxTimeoutError(this,request);//TODO: Make betterer.
327+
if(ajaxTimeoutError.response===errorObject){
328+
subscriber.error(errorObject.e);
329+
}else{
330+
subscriber.error(ajaxTimeoutError);
331+
}
324332
}
325333
xhr.ontimeout=xhrTimeout;
326334
(<any>xhrTimeout).request=request;
@@ -346,7 +354,12 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
346354
if(progressSubscriber){
347355
progressSubscriber.error(e);
348356
}
349-
subscriber.error(newAjaxError('ajax error',this,request));
357+
constajaxError=newAjaxError('ajax error',this,request);
358+
if(ajaxError.response===errorObject){
359+
subscriber.error(errorObject.e);
360+
}else{
361+
subscriber.error(ajaxError);
362+
}
350363
};
351364
xhr.onerror=xhrError;
352365
(<any>xhrError).request=request;
@@ -388,7 +401,12 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
388401
if(progressSubscriber){
389402
progressSubscriber.error(e);
390403
}
391-
subscriber.error(newAjaxError('ajax error '+status,this,request));
404+
constajaxError=newAjaxError('ajax error '+status,this,request);
405+
if(ajaxError.response===errorObject){
406+
subscriber.error(errorObject.e);
407+
}else{
408+
subscriber.error(ajaxError);
409+
}
392410
}
393411
}
394412
}
@@ -474,17 +492,21 @@ export class AjaxError extends Error {
474492
}
475493
}
476494

495+
functionparseJson(xhr: XMLHttpRequest){
496+
// HACK(benlesh): TypeScript shennanigans
497+
// tslint:disable-next-line:no-any XMLHttpRequest is defined to always have 'response' inferring xhr as never for the else clause.
498+
if('response'in(xhrasany)){
499+
//IE does not support json as responseType, parse it internally
500+
returnxhr.responseType ? xhr.response : JSON.parse(xhr.response||xhr.responseText||'null');
501+
}else{
502+
returnJSON.parse((xhrasany).responseText||'null');
503+
}
504+
}
505+
477506
functionparseXhrResponse(responseType: string,xhr: XMLHttpRequest){
478507
switch(responseType){
479508
case'json':
480-
// HACK(benlesh): TypeScript shennanigans
481-
// tslint:disable-next-line:no-any XMLHttpRequest is defined to always have 'response' inferring xhr as never for the else clause.
482-
if('response'in(xhrasany)){
483-
//IE does not support json as responseType, parse it internally
484-
returnxhr.responseType ? xhr.response : JSON.parse(xhr.response||xhr.responseText||'null');
485-
}else{
486-
returnJSON.parse((xhrasany).responseText||'null');
487-
}
509+
returntryCatch(parseJson)(xhr);
488510
case'xml':
489511
returnxhr.responseXML;
490512
case'text':

0 commit comments

Comments
 (0)