Skip to content

Commit 07e6788

Browse files
SkyZeroZxmmalerba
authored andcommitted
feat(http): Add reponseType property to HttpResponse and HttpErrorResponse (angular#63043)
Add support for the Fetch API's responseType property in HttpResponse and HttpErrorResponse when using HttpClient with the withFetch provider. PR Close angular#63043
1 parent ed3d1f2 commit 07e6788

File tree

4 files changed

+41
-1
lines changed

4 files changed

+41
-1
lines changed

goldens/public-api/common/http/index.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,7 @@ export class HttpErrorResponse extends HttpResponseBase implements Error {
25132513
statusText?: string;
25142514
url?: string;
25152515
redirected?: boolean;
2516+
responseType?: ResponseType;
25162517
});
25172518
// (undocumented)
25182519
readonly error: any | null;
@@ -2941,6 +2942,7 @@ export class HttpResponse<T> extends HttpResponseBase {
29412942
statusText?: string;
29422943
url?: string;
29432944
redirected?: boolean;
2945+
responseType?: ResponseType;
29442946
});
29452947
readonly body: T | null;
29462948
// (undocumented)
@@ -2952,6 +2954,7 @@ export class HttpResponse<T> extends HttpResponseBase {
29522954
statusText?: string;
29532955
url?: string;
29542956
redirected?: boolean;
2957+
responseType?: ResponseType;
29552958
}): HttpResponse<T>;
29562959
// (undocumented)
29572960
clone<V>(update: {
@@ -2961,6 +2964,7 @@ export class HttpResponse<T> extends HttpResponseBase {
29612964
statusText?: string;
29622965
url?: string;
29632966
redirected?: boolean;
2967+
responseType?: ResponseType;
29642968
}): HttpResponse<V>;
29652969
// (undocumented)
29662970
readonly type: HttpEventType.Response;
@@ -2974,10 +2978,12 @@ export abstract class HttpResponseBase {
29742978
statusText?: string;
29752979
url?: string;
29762980
redirected?: boolean;
2981+
responseType?: ResponseType;
29772982
}, defaultStatus?: number, defaultStatusText?: string);
29782983
readonly headers: HttpHeaders;
29792984
readonly ok: boolean;
29802985
readonly redirected?: boolean;
2986+
readonly responseType?: ResponseType;
29812987
readonly status: number;
29822988
readonly statusText: string;
29832989
readonly type: HttpEventType.Response | HttpEventType.ResponseHeader;

packages/common/http/src/fetch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ export class FetchBackend implements HttpBackend {
254254

255255
const redirected = response.redirected;
256256

257+
const responseType = response.type;
258+
257259
if (ok) {
258260
observer.next(
259261
new HttpResponse({
@@ -263,6 +265,7 @@ export class FetchBackend implements HttpBackend {
263265
statusText,
264266
url,
265267
redirected,
268+
responseType,
266269
}),
267270
);
268271

@@ -278,6 +281,7 @@ export class FetchBackend implements HttpBackend {
278281
statusText,
279282
url,
280283
redirected,
284+
responseType,
281285
}),
282286
);
283287
}

packages/common/http/src/response.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,21 @@ export abstract class HttpResponseBase {
193193
*/
194194
readonly redirected?: boolean;
195195

196+
/**
197+
* Indicates the type of the HTTP response, based on how the request was made and how the browser handles the response.
198+
*
199+
* This corresponds to the `type` property of the Fetch API's `Response` object, which can indicate values such as:
200+
* - `'basic'`: A same-origin response, allowing full access to the body and headers.
201+
* - `'cors'`: A cross-origin response with CORS enabled, exposing only safe response headers.
202+
* - `'opaque'`: A cross-origin response made with `no-cors`, where the response body and headers are inaccessible.
203+
* - `'opaqueredirect'`: A response resulting from a redirect followed in `no-cors` mode.
204+
* - `'error'`: A response representing a network error or similar failure.
205+
*
206+
* This property is only available when using the Fetch-based backend (via `withFetch()`).
207+
* When using Angular's (XHR) backend, this value will be `undefined`.
208+
*/
209+
readonly responseType?: ResponseType;
210+
196211
/**
197212
* Super-constructor for all responses.
198213
*
@@ -206,6 +221,7 @@ export abstract class HttpResponseBase {
206221
statusText?: string;
207222
url?: string;
208223
redirected?: boolean;
224+
responseType?: ResponseType;
209225
},
210226
defaultStatus: number = 200,
211227
defaultStatusText: string = 'OK',
@@ -217,7 +233,7 @@ export abstract class HttpResponseBase {
217233
this.statusText = init.statusText || defaultStatusText;
218234
this.url = init.url || null;
219235
this.redirected = init.redirected;
220-
236+
this.responseType = init.responseType;
221237
// Cache the ok value to avoid defining a getter.
222238
this.ok = this.status >= 200 && this.status < 300;
223239
}
@@ -298,6 +314,7 @@ export class HttpResponse<T> extends HttpResponseBase {
298314
statusText?: string;
299315
url?: string;
300316
redirected?: boolean;
317+
responseType?: ResponseType;
301318
} = {},
302319
) {
303320
super(init);
@@ -313,6 +330,7 @@ export class HttpResponse<T> extends HttpResponseBase {
313330
statusText?: string;
314331
url?: string;
315332
redirected?: boolean;
333+
responseType?: ResponseType;
316334
}): HttpResponse<T>;
317335
clone<V>(update: {
318336
body?: V | null;
@@ -321,6 +339,7 @@ export class HttpResponse<T> extends HttpResponseBase {
321339
statusText?: string;
322340
url?: string;
323341
redirected?: boolean;
342+
responseType?: ResponseType;
324343
}): HttpResponse<V>;
325344
clone(
326345
update: {
@@ -330,6 +349,7 @@ export class HttpResponse<T> extends HttpResponseBase {
330349
statusText?: string;
331350
url?: string;
332351
redirected?: boolean;
352+
responseType?: ResponseType;
333353
} = {},
334354
): HttpResponse<any> {
335355
return new HttpResponse<any>({
@@ -339,6 +359,7 @@ export class HttpResponse<T> extends HttpResponseBase {
339359
statusText: update.statusText || this.statusText,
340360
url: update.url || this.url || undefined,
341361
redirected: update.redirected ?? this.redirected,
362+
responseType: update.responseType ?? this.responseType,
342363
});
343364
}
344365
}
@@ -373,6 +394,7 @@ export class HttpErrorResponse extends HttpResponseBase implements Error {
373394
statusText?: string;
374395
url?: string;
375396
redirected?: boolean;
397+
responseType?: ResponseType;
376398
}) {
377399
// Initialize with a default status of 0 / Unknown Error.
378400
super(init, 0, 'Unknown Error');

packages/common/http/test/response_spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('HttpResponse', () => {
2121
statusText: 'Created',
2222
url: '/test',
2323
redirected: true,
24+
responseType: 'cors',
2425
});
2526
expect(resp.body).toBe('test body');
2627
expect(resp.headers instanceof HttpHeaders).toBeTruthy();
@@ -29,6 +30,7 @@ describe('HttpResponse', () => {
2930
expect(resp.statusText).toBe('Created');
3031
expect(resp.url).toBe('/test');
3132
expect(resp.redirected).toBe(true);
33+
expect(resp.responseType).toBe('cors');
3234
});
3335
it('uses defaults if no args passed', () => {
3436
const resp = new HttpResponse({});
@@ -39,6 +41,7 @@ describe('HttpResponse', () => {
3941
expect(resp.ok).toBeTruthy();
4042
expect(resp.url).toBeNull();
4143
expect(resp.redirected).toBeUndefined();
44+
expect(resp.responseType).toBeUndefined();
4245
});
4346
it('accepts a falsy body', () => {
4447
expect(new HttpResponse({body: false}).body).toEqual(false);
@@ -63,13 +66,15 @@ describe('HttpResponse', () => {
6366
statusText: 'created',
6467
url: '/test',
6568
redirected: false,
69+
responseType: 'cors',
6670
}).clone();
6771
expect(clone.body).toBe('test');
6872
expect(clone.status).toBe(HttpStatusCode.Created);
6973
expect(clone.statusText).toBe('created');
7074
expect(clone.url).toBe('/test');
7175
expect(clone.headers).not.toBeNull();
7276
expect(clone.redirected).toBe(false);
77+
expect(clone.responseType).toBe('cors');
7378
});
7479
it('overrides the original', () => {
7580
const orig = new HttpResponse({
@@ -78,20 +83,23 @@ describe('HttpResponse', () => {
7883
statusText: 'created',
7984
url: '/test',
8085
redirected: true,
86+
responseType: 'cors',
8187
});
8288
const clone = orig.clone({
8389
body: {data: 'test'},
8490
status: HttpStatusCode.Ok,
8591
statusText: 'Okay',
8692
url: '/bar',
8793
redirected: false,
94+
responseType: 'opaque',
8895
});
8996
expect(clone.body).toEqual({data: 'test'});
9097
expect(clone.status).toBe(HttpStatusCode.Ok);
9198
expect(clone.statusText).toBe('Okay');
9299
expect(clone.url).toBe('/bar');
93100
expect(clone.headers).toBe(orig.headers);
94101
expect(clone.redirected).toBe(false);
102+
expect(clone.responseType).toBe('opaque');
95103
});
96104
});
97105

0 commit comments

Comments
 (0)