82

( Editor: VS Code; Typescript: 2.2.1 )

The purpose is to get the headers of the response of the request

Assume a POST request with HttpClient in a Service

import {
    Injectable
} from "@angular/core";

import {
    HttpClient,
    HttpHeaders,
} from "@angular/common/http";

@Injectable()
export class MyHttpClientService {
    const url = 'url';

    const body = {
        body: 'the body'
    };

    const headers = 'headers made with HttpHeaders';

    const options = {
        headers: headers,
        observe: "response", // to display the full response
        responseType: "json"
    };

    return this.http.post(sessionUrl, body, options)
        .subscribe(response => {
            console.log(response);
            return response;
        }, err => {
            throw err;
        });
}

HttpClient Angular Documentation

The first problem is that I have a Typescript error :

'Argument of type '{ 
    headers: HttpHeaders; 
    observe: string; 
    responseType: string;
}' is not assignable to parameter of type'{ 
    headers?: HttpHeaders;
    observe?: "body";
    params?: HttpParams; reportProgress?: boolean;
    respons...'.

Types of property 'observe' are incompatible.
Type 'string' is not assignable to type '"body"'.'
at: '51,49' source: 'ts'

Indeed, when I go to the ref of post() method, I point on this prototype (I Use VS code)

post(url: string, body: any | null, options: {
        headers?: HttpHeaders;
        observe?: 'body';
        params?: HttpParams;
        reportProgress?: boolean;
        responseType: 'arraybuffer';
        withCredentials?: boolean;
    }): Observable<ArrayBuffer>;

But I want this overloaded method :

post(url: string, body: any | null, options: {
    headers?: HttpHeaders;
    observe: 'response';
    params?: HttpParams;
    reportProgress?: boolean;
    responseType?: 'json';
    withCredentials?: boolean;
}): Observable<HttpResponse<Object>>;

So, I tried to fix this error with this structure :

  const options = {
            headers: headers,
            "observe?": "response",
            "responseType?": "json",
        };

And It compiles! But I just get the body request as in json format.

Futhermore, why I have to put a ? symbol at the end of some name of fields ? As I saw on Typescript site, this symbol should just tell to the user that it is optional ?

I also tried to use all the fields, without and with ? marks

EDIT

I tried the solutions proposed by Angular 4 get headers from API response. For the map solution:

this.http.post(url).map(resp => console.log(resp));

Typescript compiler tells that map does not exists because it is not a part of Observable

I also tried this

import { Response } from "@angular/http";

this.http.post(url).post((resp: Response) => resp)

It compiles, but I get a unsupported Media Type response. These solutions should work for "Http" but it does not on "HttpClient".

EDIT 2

I get also a unsupported media type with the @Supamiu solution, so it would be an error on my headers. So the second solution from above (with Response type) should works too. But personnaly, I don't think it is a good way to mix "Http" with "HttpClient" so I will keep the solution of Supamiu

SolidCanary
  • 1,133
  • 1
  • 8
  • 15
  • Possible duplicate of [Angular 4 get headers from API response](https://stackoverflow.com/questions/44292270/angular-4-get-headers-from-api-response) – Hitmands Aug 04 '17 at 11:30
  • 3
    @Hitmands I already saw this thread, however it use "Http" and not "HttpClient" , and Angular 4.3.3 seems to tend to use HttpClient now – SolidCanary Aug 04 '17 at 11:38

7 Answers7

161

You can observe the full response instead of the content only. To do so, you have to pass observe: response into the options parameter of the function call.

http
  .get<MyJsonData>('/data.json', {observe: 'response'})
  .subscribe(resp => {
    // Here, resp is of type HttpResponse<MyJsonData>.
    // You can inspect its headers:
    console.log(resp.headers.get('X-Custom-Header'));
    // And access the body directly, which is typed as MyJsonData as requested.
    console.log(resp.body.someField);
  });

See HttpClient's documentation

Mattew Eon
  • 1,722
  • 1
  • 21
  • 38
Supamiu
  • 8,501
  • 7
  • 42
  • 76
  • Thank you! I get a unsupported data type but it would be a mistake on my headers – SolidCanary Aug 04 '17 at 12:00
  • Does anyone have an idea how to do the same for http.patch() ? It doesn't work for me. The response is empty, when I want to have the raw response object with a status code. – chriszichrisz Jan 10 '18 at 11:55
  • Okay I just found out: it's http.patch(url, params, {observe: 'response'}) and make sure the response object is of type HttpResponse – chriszichrisz Jan 10 '18 at 12:05
  • 14
    Thank you! I tried this but I'm not getting a value that appears in `Response Headers` in a network tab. – SPnL Apr 14 '18 at 05:56
  • `.get()` is not a function in Angular 7 – Kevin Beal Oct 26 '18 at 19:01
  • @ManPersonson According to the docs, it still exists (see link above). I didn't migrate my own app to 7 yet so I can't confirm tho. – Supamiu Oct 27 '18 at 12:40
  • It turns out that it doesn't work with xhr. It provides an object which partially implements `HttpHeaders` without those header access methods. My problem was I wasn't using fetch. – Kevin Beal Oct 27 '18 at 14:51
  • I tried the same but not worked. I am using spring boot as backend and Angular 8 as frontend. I need to do anything extra ? – Bhaumik Sathvara Feb 23 '20 at 10:40
  • This is actually an incorrect answer. The OP said, "assume a POST" which is the actual problem -- GET allows you to do {observe: 'response'} but POST does not -- so this is a misleading answer. – John Q May 19 '20 at 20:10
  • According to the documentation, it was possible when I posted this reply and it's still possible: https://angular.io/guide/http#making-a-post-request. However, if you want to make sure you can observe the response, you can use the `request` method directly. – Supamiu May 20 '20 at 10:13
  • For GET: observe?: 'response' | 'body'; For POST: observe?: 'body'; You may be pointing out that something else about your answer works because 'response' doesn't work for POST (which is why the OP had trouble with it). To use 'reponse' with POST, you have to use Birbal Singh's answer below: observe: "response" as 'body' – John Q May 24 '20 at 20:22
  • 1
    That's way too hacky for me tbh, if this is the only way to make it work, and makes it work, it means that it's possible and should be integrated to the API, I'd report it as an issue. – Supamiu May 25 '20 at 07:05
  • I agree with that. Seems like something is missing. Hence OP's question. :) – John Q May 30 '20 at 23:14
30

main problem of typecast so we can use "response" as 'body'

we can handle like

const options = {
    headers: headers,
    observe: "response" as 'body', // to display the full response & as 'body' for type cast
    responseType: "json"
};

return this.http.post(sessionUrl, body, options)
    .subscribe(response => {
        console.log(response);
        return response;
    }, err => {
        throw err;
    });
Birbal Singh
  • 1,062
  • 7
  • 16
16

Indeed, the main problem was a Typescript problem.

In the code of post(), options was declared directly in the parameters, so, as an "anonymous" interface.

The solution was to put directly the options in raw inside the parameters

http.post("url", body, {headers: headers, observe: "response"}).subscribe...
SolidCanary
  • 1,133
  • 1
  • 8
  • 15
  • 2
    life saver - this was driving me nuts. Still don't get why inlining the options hash works but post("url", body, options) doesn't. But yay! – Gishu Dec 11 '17 at 12:33
  • 1
    @Gishu, the reason is explained in [this answer](https://stackoverflow.com/a/47761516/3291390). A solution without inlining that worked great for me is in [this answer](https://stackoverflow.com/a/48951705/3291390), although some changes may need to be made to the interface to make it match what you need. – Stack Underflow Jun 21 '18 at 22:24
  • this should work, but it doesn't work on my side, the "post method" is simply not being called, not sending or receiving anything in this setup, it works only if i put {observe: "response"} as the 3rd parameter – aero Oct 03 '18 at 15:32
3

If you use the solution from the top answer and you don't have access to .keys() or .get() on response.headers, make sure that you're using fetch rather than xhr.

Fetch requests are the default, but Angular will use xhr if xhr-only headers are present (e.x. x-www-form-urlencoded).

If you are trying to access any custom response headers, then you have to specify those headers with another header called Access-Control-Expose-Headers.

Kevin Beal
  • 10,500
  • 12
  • 66
  • 92
3

Some times even with the above solution you can't retrieve custom headers if it is CORS request. In that case you need to whitelist desired headers in server side.

For Example: Access-Control-Expose-Headers: X-Total-Count

3

The below method worked perfectly for me (currently Angular 10). It also avoids setting some arbitary filename, instead it gets the filename from the content-disposition header.

this._httpClient.get("api/FileDownload/GetFile", { responseType: 'blob' as 'json', observe: 'response' }).subscribe(response =>  { 
    /* Get filename from Content-Disposition header */
    var filename = "";
    var disposition = response.headers.get('Content-Disposition');
    if (disposition && disposition.indexOf('attachment') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
    }
    // This does the trick
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(response.body);
    a.download = filename;
    a.dispatchEvent(new MouseEvent('click'));
})
Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102
0

As other developers said, for getting headers and body together you should define the type of observer yield in this way:

http.post("url", body, {headers: headers, observe: "response" as "body"})

Then you can access to body and headers in pip or a subscribe area:

http.post("url", body, {headers: headers, observe: "response" as "body"})
.pip(
  tap(res => {
   // res.headers
   // res.body
  })
)
.subscribe(res => {
   // res.headers
   // res.body
})
Navid Shad
  • 738
  • 7
  • 15