-
Notifications
You must be signed in to change notification settings - Fork 27.1k
feat(common): support custom JSON parser in HttpClient #25027
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |||||||||
| * found in the LICENSE file at https://angular.io/license | ||||||||||
| */ | ||||||||||
|
|
||||||||||
| import {Injectable} from '@angular/core'; | ||||||||||
| import {Inject, Injectable, InjectionToken} from '@angular/core'; | ||||||||||
| import {Observable, Observer} from 'rxjs'; | ||||||||||
|
|
||||||||||
| import {HttpBackend} from './backend'; | ||||||||||
|
|
@@ -16,6 +16,13 @@ import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, | |||||||||
|
|
||||||||||
| const XSSI_PREFIX = /^\)\]\}',?\n/; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Utility for parsing JSON text to JavaScript object | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we would need a bit more documentation for this and the |
||||||||||
| */ | ||||||||||
| export interface JsonParser { parse(text: string, reviver?: (key: any, value: any) => any): any; } | ||||||||||
|
|
||||||||||
| export const JSON_PARSER = new InjectionToken<JsonParser>('JsonParser'); | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Determine an appropriate URL for the response, by checking either | ||||||||||
| * XMLHttpRequest.responseURL or the X-Request-URL header. | ||||||||||
|
|
@@ -70,7 +77,8 @@ interface PartialResponse { | |||||||||
| */ | ||||||||||
| @Injectable() | ||||||||||
| export class HttpXhrBackend implements HttpBackend { | ||||||||||
| constructor(private xhrFactory: XhrFactory) {} | ||||||||||
| constructor(private xhrFactory: XhrFactory, @Inject(JSON_PARSER) private jsonParser: JsonParser) { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about making this optional, then we wouldn't have to define a provider for the default case:
Suggested change
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Processes a request and returns a stream of response events. | ||||||||||
|
|
@@ -192,7 +200,7 @@ export class HttpXhrBackend implements HttpBackend { | |||||||||
| body = body.replace(XSSI_PREFIX, ''); | ||||||||||
| try { | ||||||||||
| // Attempt the parse. If it fails, a parse error should be delivered to the user. | ||||||||||
| body = body !== '' ? JSON.parse(body) : null; | ||||||||||
| body = body !== '' ? this.jsonParser.parse(body) : null; | ||||||||||
| } catch (error) { | ||||||||||
| // Since the JSON.parse failed, it's reasonable to assume this might not have been a | ||||||||||
| // JSON response. Restore the original body (including any XSSI prefix) to deliver | ||||||||||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ | |
|
|
||
| import {HttpRequest} from '@angular/common/http/src/request'; | ||
| import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpUploadProgressEvent} from '@angular/common/http/src/response'; | ||
| import {HttpXhrBackend} from '@angular/common/http/src/xhr'; | ||
| import {HttpXhrBackend, JsonParser} from '@angular/common/http/src/xhr'; | ||
| import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal'; | ||
| import {Observable} from 'rxjs'; | ||
| import {toArray} from 'rxjs/operators'; | ||
|
|
@@ -33,7 +33,7 @@ const XSSI_PREFIX = ')]}\'\n'; | |
| let backend: HttpXhrBackend = null!; | ||
| beforeEach(() => { | ||
| factory = new MockXhrFactory(); | ||
| backend = new HttpXhrBackend(factory); | ||
| backend = new HttpXhrBackend(factory, JSON); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
| }); | ||
| it('emits status immediately', () => { | ||
| const events = trackEvents(backend.handle(TEST_POST)); | ||
|
|
@@ -94,6 +94,19 @@ const XSSI_PREFIX = ')]}\'\n'; | |
| const res = events[1] as HttpResponse<{data: string}>; | ||
| expect(res.body!.data).toBe('some data'); | ||
| }); | ||
| it('supports custom json parser', () => { | ||
| const parser: JsonParser = { | ||
| parse() { | ||
| return 'JSON_RESULT'; | ||
| } | ||
| }; | ||
| backend = new HttpXhrBackend(factory, parser); | ||
| const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); | ||
| factory.mock.mockFlush(200, 'OK', JSON.stringify({data: 'NOT USED'})); | ||
| expect(events.length).toBe(2); | ||
| const res = events[1] as HttpResponse<string>; | ||
| expect(res.body).toBe('JSON_RESULT'); | ||
| }); | ||
| it('handles a blank json response', () => { | ||
| const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); | ||
| factory.mock.mockFlush(200, 'OK', ''); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
jsonParserwas optional then this would not be needed.