mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(http): Use the response content-type to set the blob type. (#52840)
When downloading a PDF with the fetch client, the blob had no content. It couldn't be displayed in an iframe. This commit fixes this. Relate to: https://stackoverflow.com/questions/77470626/possible-bug-in-httpclient-when-using-the-blob-data-type PR Close #52840
This commit is contained in:
committed by
Jessica Janiuk
parent
6a1d4ed667
commit
7c066a4af4
@@ -145,7 +145,8 @@ export class FetchBackend implements HttpBackend {
|
||||
// Combine all chunks.
|
||||
const chunksAll = this.concatChunks(chunks, receivedLength);
|
||||
try {
|
||||
body = this.parseBody(request, chunksAll);
|
||||
const contentType = response.headers.get('Content-Type') ?? '';
|
||||
body = this.parseBody(request, chunksAll, contentType);
|
||||
} catch (error) {
|
||||
// Body loading or parsing failed
|
||||
observer.error(new HttpErrorResponse({
|
||||
@@ -193,8 +194,8 @@ export class FetchBackend implements HttpBackend {
|
||||
}
|
||||
}
|
||||
|
||||
private parseBody(request: HttpRequest<any>, binContent: Uint8Array): string|ArrayBuffer|Blob
|
||||
|object|null {
|
||||
private parseBody(request: HttpRequest<any>, binContent: Uint8Array, contentType: string): string
|
||||
|ArrayBuffer|Blob|object|null {
|
||||
switch (request.responseType) {
|
||||
case 'json':
|
||||
// stripping the XSSI when present
|
||||
@@ -203,7 +204,7 @@ export class FetchBackend implements HttpBackend {
|
||||
case 'text':
|
||||
return new TextDecoder().decode(binContent);
|
||||
case 'blob':
|
||||
return new Blob([binContent]);
|
||||
return new Blob([binContent], {type: contentType});
|
||||
case 'arraybuffer':
|
||||
return binContent.buffer;
|
||||
}
|
||||
|
||||
@@ -217,6 +217,16 @@ describe('FetchBackend', async () => {
|
||||
expect(res.body!.data).toBe('some data');
|
||||
});
|
||||
|
||||
it('handles a blob with a mime type', async () => {
|
||||
const promise = trackEvents(backend.handle(TEST_POST.clone({responseType: 'blob'})));
|
||||
const type = 'aplication/pdf';
|
||||
fetchMock.mockFlush(HttpStatusCode.Ok, 'OK', new Blob(), {'Content-Type': type});
|
||||
const events = await promise;
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<Blob>;
|
||||
expect(res.body?.type).toBe(type);
|
||||
});
|
||||
|
||||
it('emits unsuccessful responses via the error path', done => {
|
||||
backend.handle(TEST_POST).subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
@@ -400,12 +410,18 @@ export class MockFetchFactory extends FetchFactory {
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
mockFlush(status: number, statusText: string, body?: string): void {
|
||||
mockFlush(
|
||||
status: number, statusText: string, body?: string|Blob, headers?: Record<string, string>):
|
||||
void {
|
||||
this.clearWarningTimeout?.();
|
||||
this.response.setupBodyStream(body);
|
||||
|
||||
const response =
|
||||
new Response(this.response.stream, {statusText, headers: this.response.headers});
|
||||
if (typeof body === 'string') {
|
||||
this.response.setupBodyStream(body);
|
||||
} else {
|
||||
this.response.setBody(body);
|
||||
}
|
||||
const response = new Response(
|
||||
this.response.stream,
|
||||
{statusText, headers: {...this.response.headers, ...(headers ?? {})}});
|
||||
|
||||
// Have to be set outside the constructor because it might throw
|
||||
// RangeError: init["status"] must be in the range of 200 to 599, inclusive
|
||||
@@ -470,6 +486,11 @@ class MockFetchResponse {
|
||||
},
|
||||
});
|
||||
|
||||
public setBody(body: any) {
|
||||
this.sub$.next(body);
|
||||
this.sub$.complete();
|
||||
}
|
||||
|
||||
public setupBodyStream(body?: string) {
|
||||
if (body && this.progress.length) {
|
||||
this.headers['content-length'] = `${body.length}`;
|
||||
|
||||
Reference in New Issue
Block a user