angular2/http 获取 201 响应的位置 header
angular2/http get location header of 201 response
我成功完成了 angular2 "tour of heroes" 入门教程。然后我构建了一个基于 symfony3、FosRestBundle 和 BazingaHateoasBundle 作为后端的 api。
现在几乎一切正常,但在创建新项目时,我无法从响应中获取 "Location" header 以加载新创建的项目。
这是我的英雄服务:
import {Injectable} from "angular2/core";
import {Hero} from "./hero";
import {Http, Headers, RequestOptions, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import "rxjs/Rx";
@Injectable()
export class HeroService {
constructor(private _http:Http) {
}
private _apiUrl = 'http://tour-of-heros.loc/app_dev.php/api'; // Base URL
private _heroesUrl = this._apiUrl + '/heroes'; // URL to the hero api
getHeroes() {
return this._http.get(this._heroesUrl)
.map(res => <Hero[]> res.json()._embedded.items)
.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
addHero(name:string):Observable<Hero> {
let body = JSON.stringify({name});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this._http.post(this._heroesUrl, body, options)
// Hero is created, but unable to get URL from the Location header!
.map((res:Response) => this._http.get(res.headers.get('Location')).map((res:Response) => res.json()))
.catch(this.handleError)
}
private handleError(error:Response) {
// in a real world app, we may send the error to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
getHero(id:number) {
return this._http.get(this._heroesUrl + '/' + id)
.map(res => {
return res.json();
})
.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
}
因此,当我调用 addHero 方法时,会创建一个新英雄并返回一个没有 body 的 201 响应,但会设置位置 header:
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 0
Content-Type: application/json
Date: Tue, 29 Mar 2016 09:24:42 GMT
Keep-Alive: timeout=5, max=100
Location: http://tour-of-heros.loc/app_dev.php/api/heroes/3
Server: Apache/2.4.10 (Debian)
X-Debug-Token: 06323e
X-Debug-Token-Link: /app_dev.php/_profiler/06323e
access-control-allow-credentials: true
access-control-allow-origin: http://172.18.0.10:3000
问题是,res.headers.get('Location')
没有从响应中获取位置 header。
经过一些调试后,res.headers
似乎只提供了两个 headers Cache-Control 和 Content-Type。但是没有位置。
我知道通过将新创建的数据添加到响应的 body 中应该也可以解决问题,但这并不是我真正想要解决的方法。
提前致谢!
您应该使用 flatMap
运算符而不是 map
运算符:
return this._http.post(this._heroesUrl, body, options)
.flatMap((res:Response) => {
var location = res.headers.get('Location');
return this._http.get(location);
}).map((res:Response) => res.json()))
.catch(this.handleError)
编辑
关于您的 header 问题。
我认为您的问题与 CORS 有关。我认为预检请求应该 return headers 在其响应中的 Access-Control-Allow-Headers
中授权。类似的东西:
Access-Control-Allow-Headers:location
如果您在呼叫的服务器端有手,您可以用您想要在客户端使用的 header 更新此 header(in your case
Location `).
我调试了请求,特别是在实际执行请求并从 XHR object 获取响应的 XhrBackend
class 中。 header 不是由代码 return 编辑的:_xhr.getAllResponseHeaders()
。就我而言,我只有 Content-Type 个。
看到这个 link:https://github.com/angular/angular/blob/master/modules/angular2/src/http/backends/xhr_backend.ts#L42。
来自以下问题:
Cross-Origin Resource Sharing specification filters the headers that are exposed by getResponseHeader() for non same-origin requests. And that specification forbids access to any response header field other except the simple response header fields (i.e. Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma):
有关详细信息,请参阅此问题:
我的错!
阅读后 https://github.com/angular/angular/issues/5237 and http://www.aaron-powell.com/posts/2013-11-28-accessing-location-header-in-cors-response.html, I recognize that there must be something wrong with my CORS configuration of the NelmioCorsBundle (https://github.com/nelmio/NelmioCorsBundle/issues/9)。
作为快速修复,我将 expose_headers: ['Origin','Accept','Content-Type', 'Location']
添加到 symfony 配置中:
之前:
nelmio_cors:
paths:
'^/api/':
allow_credentials: true
origin_regex: true
allow_origin: ['*']
allow_headers: ['Origin','Accept','Content-Type', 'Location']
allow_methods: ['POST','GET','DELETE','PUT','OPTIONS']
max_age: 3600
之后:
nelmio_cors:
paths:
'^/api/':
allow_credentials: true
origin_regex: true
allow_origin: ['*']
allow_headers: ['Origin','Accept','Content-Type', 'Location']
allow_methods: ['POST','GET','DELETE','PUT','OPTIONS']
expose_headers: ['Origin','Accept','Content-Type', 'Location']
max_age: 3600
现在它就像一个魅力。
所以这不是 angular 问题,而且从来没有真正解决过。对不起!我会为每个遇到类似 CORS 问题的人保留这个。
非常感谢所有贡献者
我成功完成了 angular2 "tour of heroes" 入门教程。然后我构建了一个基于 symfony3、FosRestBundle 和 BazingaHateoasBundle 作为后端的 api。 现在几乎一切正常,但在创建新项目时,我无法从响应中获取 "Location" header 以加载新创建的项目。
这是我的英雄服务:
import {Injectable} from "angular2/core";
import {Hero} from "./hero";
import {Http, Headers, RequestOptions, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import "rxjs/Rx";
@Injectable()
export class HeroService {
constructor(private _http:Http) {
}
private _apiUrl = 'http://tour-of-heros.loc/app_dev.php/api'; // Base URL
private _heroesUrl = this._apiUrl + '/heroes'; // URL to the hero api
getHeroes() {
return this._http.get(this._heroesUrl)
.map(res => <Hero[]> res.json()._embedded.items)
.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
addHero(name:string):Observable<Hero> {
let body = JSON.stringify({name});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this._http.post(this._heroesUrl, body, options)
// Hero is created, but unable to get URL from the Location header!
.map((res:Response) => this._http.get(res.headers.get('Location')).map((res:Response) => res.json()))
.catch(this.handleError)
}
private handleError(error:Response) {
// in a real world app, we may send the error to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
getHero(id:number) {
return this._http.get(this._heroesUrl + '/' + id)
.map(res => {
return res.json();
})
.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
}
因此,当我调用 addHero 方法时,会创建一个新英雄并返回一个没有 body 的 201 响应,但会设置位置 header:
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 0
Content-Type: application/json
Date: Tue, 29 Mar 2016 09:24:42 GMT
Keep-Alive: timeout=5, max=100
Location: http://tour-of-heros.loc/app_dev.php/api/heroes/3
Server: Apache/2.4.10 (Debian)
X-Debug-Token: 06323e
X-Debug-Token-Link: /app_dev.php/_profiler/06323e
access-control-allow-credentials: true
access-control-allow-origin: http://172.18.0.10:3000
问题是,res.headers.get('Location')
没有从响应中获取位置 header。
经过一些调试后,res.headers
似乎只提供了两个 headers Cache-Control 和 Content-Type。但是没有位置。
我知道通过将新创建的数据添加到响应的 body 中应该也可以解决问题,但这并不是我真正想要解决的方法。
提前致谢!
您应该使用 flatMap
运算符而不是 map
运算符:
return this._http.post(this._heroesUrl, body, options)
.flatMap((res:Response) => {
var location = res.headers.get('Location');
return this._http.get(location);
}).map((res:Response) => res.json()))
.catch(this.handleError)
编辑
关于您的 header 问题。
我认为您的问题与 CORS 有关。我认为预检请求应该 return headers 在其响应中的 Access-Control-Allow-Headers
中授权。类似的东西:
Access-Control-Allow-Headers:location
如果您在呼叫的服务器端有手,您可以用您想要在客户端使用的 header 更新此 header(in your case
Location `).
我调试了请求,特别是在实际执行请求并从 XHR object 获取响应的 XhrBackend
class 中。 header 不是由代码 return 编辑的:_xhr.getAllResponseHeaders()
。就我而言,我只有 Content-Type 个。
看到这个 link:https://github.com/angular/angular/blob/master/modules/angular2/src/http/backends/xhr_backend.ts#L42。
来自以下问题:
Cross-Origin Resource Sharing specification filters the headers that are exposed by getResponseHeader() for non same-origin requests. And that specification forbids access to any response header field other except the simple response header fields (i.e. Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma):
有关详细信息,请参阅此问题:
我的错!
阅读后 https://github.com/angular/angular/issues/5237 and http://www.aaron-powell.com/posts/2013-11-28-accessing-location-header-in-cors-response.html, I recognize that there must be something wrong with my CORS configuration of the NelmioCorsBundle (https://github.com/nelmio/NelmioCorsBundle/issues/9)。
作为快速修复,我将 expose_headers: ['Origin','Accept','Content-Type', 'Location']
添加到 symfony 配置中:
之前:
nelmio_cors:
paths:
'^/api/':
allow_credentials: true
origin_regex: true
allow_origin: ['*']
allow_headers: ['Origin','Accept','Content-Type', 'Location']
allow_methods: ['POST','GET','DELETE','PUT','OPTIONS']
max_age: 3600
之后:
nelmio_cors:
paths:
'^/api/':
allow_credentials: true
origin_regex: true
allow_origin: ['*']
allow_headers: ['Origin','Accept','Content-Type', 'Location']
allow_methods: ['POST','GET','DELETE','PUT','OPTIONS']
expose_headers: ['Origin','Accept','Content-Type', 'Location']
max_age: 3600
现在它就像一个魅力。
所以这不是 angular 问题,而且从来没有真正解决过。对不起!我会为每个遇到类似 CORS 问题的人保留这个。
非常感谢所有贡献者