我使用 HttpClient.get() 从 Angular 9 应用程序调用 REST 服务,但没有看到响应标头的完整列表。我知道该服务正在发送它们,因为:
和
java.net.http.HttpHeaders@1627d314 { {access-control-allow-origin=[*], 年龄=[0],连接=[保持活动],内容长度=[1207], content-type=[application/json], date=[2020 年 7 月 7 日星期二 05:11:45 GMT] <...etc>
我从 Angular HttpClient.get() 得到的只是 header.keys() 中的一项:
headers: {
"normalizedNames": {},
"lazyUpdate": null,
"lazyInit": null,
"headers": {}
}
headerKeys:
[
"content-type: application/json"
]
我创建了一个小示例应用程序来演示该问题。以下是关键组件:
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 { }
测试器.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);
}
));
}
}
附录:
为了进一步说明这个问题,这里有一个小型 Java 11 程序,它使用相同的凭据“完全”调用相同的 REST API。您可以从输出中看到 REST API 正在发回所有标头响应信息。问题仍然存在,为什么 Angular 程序无法准确地调用相同的 REST API 查看完整的响应标头?通话中是否缺少某些设置/标志/巫毒?
Java 11 应用程序:
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("------------------");
}
}
Java 应用程序的输出(response.header 仅出于简洁目的):
response.headers: java.net.http.HttpHeaders@96f4f3fc { {access-control-allow-origin=[*],年龄=[0],连接=[keep-alive], 内容长度=[1302],内容类型=[应用程序/json],日期=[星期三,08 2020 年 7 月 17:13:42 GMT],服务器=[openresty], 严格传输安全=[最大年龄=31536000;预载], 变化=[接受编码],via=[http/1.1 api-umbrella (ApacheTrafficServer [cMsSf ])], x-cache=[MISS], x-ratelimit-limit=[40], x-ratelimit-remaining=[39]} }
感谢您的帮助!
您需要设置
Access-Control-Expose-Headers
缓存控制内容语言
标头中。
- 内容长度
- 内容类型
- 过期
- 最后修改时间
- 杂注
- 您将在网络选项卡中看到其他标头,但 JavaScript 将无法访问它们,除非它们位于
Access-Control-Expose-Headers
这是 Axios 的一个类似问题,这是 4 年前不同的 javascript HTTP 库,具有相同的答案:
Axios 可以访问响应头字段
And 和 2.5 年前的 Angular 从 API 响应中读取响应标头 - Angular 5 + TypeScript
更新:由于您正在调用一个我认为您不拥有的API(https://api.nasa.gov/),因此如果您需要阅读,您必须让NASA添加标头
Access-Control-Expose-Headers: X-RateLimit-Limit, X-RateLimit-Remaining
您客户端中的那些标头。 您的另一个选择是创建一个代理服务器,而不是调用 NASA,您可以调用自己的服务器,该服务器将调用 NASA 并且可以包含您的速率限制标头。
为了获取 HTTP 响应的完整标头以及内容,您应该在发出请求时传递另一个名为
observehttp
.get<MyJsonData>('/data.json', {observe: 'response'})
.subscribe(response => {
response.headers.keys().map( (key) => console.log(`${key}: ${response.headers.get(key)}`));
});
我花了几个小时进行反复试验才使其发挥作用。在我看来,Chrome 和 javascript 的交互方式很古怪。
但是 Chrome 将其显示为“X-Document-Id”。
JavaScript 看不到标题
除非我公开“X-Document-Id”,如下所示:
@CrossOrigin(
origins = {
"http://localhost:4200",
"https://www.xxxxxxx.com"
},
allowCredentials = "true",
exposedHeaders = {
"X-Document-Id",
"x-document-id"
}
)
但是当我转储 JSON.stringify( resp.headers.keys() ) 时,控制台显示:
[
"cache-control",
"content-length",
"content-type",
"x-document-id"
]
And when I dump JSON.stringify( resp.headers ) the console displays:
{
"normalizedNames": {},
"lazyUpdate": null,
"lazyInit": null,
"headers": {}
}
系统的某些部分真的区分大小写而其他部分不区分大小写吗?
来自 mozila
文档“Access-Control-Expose-Headers 响应标头允许服务器指示哪些响应标头应可供浏览器中运行的脚本使用,以响应跨源请求。 默认情况下仅公开 CORS 安全列表中的响应标头。为了使客户端能够访问其他标头,服务器必须使用 Access-Control-Expose-Headers 标头列出它们。”
您需要配置服务器端api(core.api,exc')
显式允许您想要向客户端公开的标头
services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyHeader()
.AllowAnyOrigin()
.WithExposedHeaders("Content-Range");
});
});
配置后,您将看到标题“content-Range”
content-range: Categories 0-2/4
它将在 JavaScript 代码中可用