7

我成功完成了angular2“英雄之旅”入门教程。然后我构建了一个基于 symfony3、FosRestBundle 和 BazingaHateoasBundle 的 api 作为后端。现在几乎一切正常,但是在创建新项目时,我无法从响应中获取“位置”标头以加载新创建的项目。

这是我的英雄服务:

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 方法时,会创建一个新的 Hero 并返回一个没有正文的 201 响应,但设置了 Location 标头:

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')没有从响应中获取 Location 标头。经过一些调试,似乎res.headers只是提供了两个标头 Cache-Control 和 Content-Type。但没有位置。

我知道应该也可以通过将新创建的数据添加到响应正文来解决问题,但这并不是我真正想要解决的方法。

先感谢您!

4

2 回答 2

11

您应该使用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)

编辑

关于你的标题问题。

我认为您的问题与CORS有关。我认为预检请求应Access-Control-Allow-Headers在其响应中返回要授权的标头。像这样的东西:

Access-Control-Allow-Headers:location

如果您在调用的服务器端有手,则可以使用要在客户端使用的标头 ( in your caseLocation`) 更新此标头。

我调试了请求,特别是在XhrBackend实际执行请求并从 XHR 对象获取响应的类中。代码不返回标头:_xhr.getAllResponseHeaders(). 就我而言,我只有 Content-Type 之一。

请参阅此链接:https ://github.com/angular/angular/blob/master/modules/angular2/src/http/backends/xhr_backend.ts#L42 。

从以下问题:

跨域资源共享规范过滤 getResponseHeader() 为非同源请求公开的标头。并且该规范禁止访问除简单响应头字段(即 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified 和 Pragma)之外的任何响应头字段:

有关更多详细信息,请参阅此问题:

于 2016-03-29T10:38:37.280 回答
4

我的错!

阅读https://github.com/angular/angular/issues/5237http://www.aaron-powell.com/posts/2013-11-28-accessing-location-header-in-cors-response 后。 html,我认识到我的 NelmioCorsBundle 的 CORS 配置一定有问题(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

现在它就像一个魅力。

所以这不是一个角度问题,也从来没有真正解决过。对不起!我会为所有遇到类似 CORS 问题的人保留这个。

非常感谢所有贡献的人

于 2016-03-29T11:35:13.347 回答