Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular HttpClient.get() Not Returning Full Response Headers

I'm calling a REST service from an Angular 9 app using HttpClient.get() and I'm not seeing a full list of the response headers. I know that the service is sending them because:

  1. I can see them in the browser debugger Network=>Headers (see image) browser debugger image

and

  1. when I hit the same REST service with a Java app it returns the full headers, about a dozen all together:

java.net.http.HttpHeaders@1627d314 { {access-control-allow-origin=[*], age=[0], connection=[keep-alive], content-length=[1207], content-type=[application/json], date=[Tue, 07 Jul 2020 05:11:45 GMT] <...etc>

What I get from the Angular HttpClient.get() is only one item in the header.keys():

headers: {
  "normalizedNames": {},
  "lazyUpdate": null,
  "lazyInit": null,
  "headers": {}
}

headerKeys:
[
  "content-type: application/json"
]

I created a small sample app to demonstrate the problem. Here are the key components:

app.modules.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TesterComponent } from './tester/tester.component';

@NgModule({
  declarations: [
    AppComponent,
    TesterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

tester.component.ts:

import { Component, OnInit } from '@angular/core';
import { HttpHeaders, HttpParams, HttpResponse, HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Component({
  selector: 'app-tester',
  templateUrl: './tester.component.html',
  styleUrls: ['./tester.component.css']
})
export class TesterComponent implements OnInit {

  _url: string = "https://api.nasa.gov/planetary/apod";
  _api_key: string="DEMO_KEY";

  //
_title: string;
_date: string;

  constructor(private _httpClient: HttpClient) { }

  ngOnInit(): void {

    this.GetData(this._url).subscribe(()  =>
    {
       // do other stuff
     
    });
  }

  
 sendGetRequest(getUrl: string, headers: HttpHeaders, urlParams: HttpParams) : Observable<HttpResponse<Object>>{
    return this._httpClient.get<HttpResponse<Object>>(getUrl, {headers: headers, params: urlParams, observe: 'response'});
  }


  GetData(url: string)
  {

    const params = new HttpParams()
      .set("api_key", this._api_key);

    return this.sendGetRequest(url, headers, params).pipe(
      
      tap( response =>
      {
      
      console.log("returning data");

      if (response.headers)
      {
        console.log('headers', response.headers);
      }

      const keys = response.headers.keys();

      if (keys)
      {
        const headerKeys = keys.map(key =>
          `${key}: ${response.headers.get(key)}`);

        console.log('headerKeys', headerKeys);
      }

      this._date = response.body['date'];
      this._title = response.body['title'];
    },
    err => {
      console.log(err);
    }
    
      ));
  }

}

Addendum: To further illustrate the problem here is a small Java 11 program that calls exactly the same REST API with the same credentials. You can see from the output that the REST API is sending back all of the header response information. The question remains, why can't the Angular program calling exactly the same REST API see the full response headers? Is there some setting/flag/voodoo missing from the call?

Java 11 app:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;

public class MainClass {

    public static void main(String[] args) {
        String api_key = "DEMO_KEY";
        String uri = "https://api.nasa.gov/planetary/apod";

        uri += "?api_key=" + api_key;

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri)).build();

        HttpResponse<String> response = null;

        try {
            response = client.send(request, BodyHandlers.ofString());
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("------------------");
        System.out.println("response.headers: " + response.headers());
        System.out.println(response.body());
        System.out.println("------------------");
    }

}

Output from Java app (response.header only for brevity):

response.headers: java.net.http.HttpHeaders@96f4f3fc { {access-control-allow-origin=[*], age=[0], connection=[keep-alive], content-length=[1302], content-type=[application/json], date=[Wed, 08 Jul 2020 17:13:42 GMT], server=[openresty], strict-transport-security=[max-age=31536000; preload], vary=[Accept-Encoding], via=[http/1.1 api-umbrella (ApacheTrafficServer [cMsSf ])], x-cache=[MISS], x-ratelimit-limit=[40], x-ratelimit-remaining=[39]} }

Thanks for your help!

like image 564
Ken Avatar asked Oct 16 '25 03:10

Ken


1 Answers

You will need to set Access-Control-Expose-Headers header to include the additional headers. See Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

By default, only the 7 CORS-safelisted response headers are exposed:

  • Cache-Control
  • Content-Language
  • Content-Length
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

You will see your other headers in the network tab, but JavaScript will not have access to them unless they are in the Access-Control-Expose-Headers header.

Heres a similar question for Axios, a different javascript HTTP library from 4 years ago with the same answer: Axios get access to response header fields

And and Angular one from 2.5 years ago Read response headers from API response - Angular 5 + TypeScript

UPDATE: Since you are calling an API that I assume you don't own (https://api.nasa.gov/), you'll have to have NASA add the header Access-Control-Expose-Headers: X-RateLimit-Limit, X-RateLimit-Remaining if you need to read those headers in your client.

Your other option is to create a proxy server and instead of calling NASA, you call your own server which will call NASA and can include your rate limit headers.

like image 83
cjd82187 Avatar answered Oct 17 '25 18:10

cjd82187



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!