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:
and
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With